본문 바로가기
IT_정보보안/웹 모의해킹 프로젝트

36. 웹 모의해킹 실습 (3) : 파일 업로드, Insecure CAPTCHA

by jys275 2024. 10. 27.

 

 

파일 업로드(File Upload)

 

 

개념
웹 애플리케이션에 파일 업로드 기능이 존재하고, 

업로드 파일에 대한 필터링 조치가 미흡하여, 서버 측에서 실행될 수 있는 

 

서버 사이드 스크립트 파일(asp, jsp, php 파일 등)을 업로드가 가능해서 발생할 수 있는 취약점을 의미한다.

 

이때, 게시판 내에 첨부파일이 업로드된 파일 경로를 확인하고, 해당 경로에 접근한 후 서버 상의 웹 쉘을 실행하여, 

시스템 내부 명령어를 실행하거나 권한을 획득하고, 외부와 연결하여 시스템을 제어할 수 있다.

 

해당 취약점을 통해 서버에 영향을 미쳐서 권한 상승, 정보 유출,

악성코드 배포 등의 여러 보안 사고가 발생할 수 있으므로, 매우 크리티컬 한 취약점이라고 할 수 있다.

 

실습(Low Level)

아래는 Low Level의 파일 업로드 실습 페이지이다. 

‘Choose File’, ‘Upload’ 버튼이 존재하는 것을 확인할 수 있다.

 

사용자에게 명령어를 입력할 수 있는 HTML 폼을 제공하여 cmd라는 이름의 파라미터로 입력을 받고, 

명령어가 cmd 파라미터로 전달되면, system() 함수를 통해 해당 명령어를 서버에서 실행하고,

 

그 결과를 화면에 출력하는 ‘webshell.php’ 파일을 작성한 후, 업로드를 실시한다.

 

Low Level에서는 위와 같이 업로드를 하면, 

“../../hackable/uploads/webshell.php succesfully uploaded!”라는 결과가 출력되는 것을 확인할 수 있다. 

현재 실습 페이지의 URL은 “http://localhost/DVWA/vulnerabilities/upload/#”이고,

출력 메시지에서 두 번의 상위 디렉토리로 이동한 것을 알 수 있으므로,

 

webshell.php 파일이 업로드된 경로는 “http://localhost/DVWA/hackable/uploads/webshell.php"가 된다.
이를 바탕으로 파일이 업로드된 경로를 URL에 입력하여 php을 실행시킨다.

 

정상적으로 php 파일이 실행된 것을 알 수 있으며, 명령어 중 /etc/passwd 파일의 내용을 출력하는 

“cat /etc/passwd” 명령어를 입력하였을 때, 결과가 그대로 출력되는 것 또한 확인 가능하다.

실습(Medium Level)

Medium Level에서는 같은 파일을 업로드할 시,

 

“Your image was not uploaded. We can only accept JPEG or PNG images.”라는 메시지가 출력되며,

JPEG 또는 PNG 파일만 업로드가 허용된다고 한다.

이를 우회하기 위해 cmd.php를 cmd.php.jpeg로 변경해서 업로드를 시도할 수 있다.

하지만, 확장자가 jpeg이므로, 실제로는 이미지가 아님에도 불구하고 파일 형식이 image/jpeg가 된다.

 

HTTP 통신에서, 자바스크립트를 포함하여 웹 브라우저가 결정하는 모든 값은

서버로 전달하기 직전에 프록시를 통해서 조작이 가능하다.

 

즉, 우리는 Burp Suite 프록시 툴을 통하여 HTTP 요청을 조작할 수 있을 것이다.

Content-type은 이미 image/jpeg로 설정된 것을 확인할 수 있지만,

파일을 jpeg로 그대로 전달하면 확장자가 이미지 파일이기 때문에 정상적으로 웹 셀을 실행할 수 없다.

 

이를 해결하기 위해 Burp Suite를 통해 파일 업로드 시 발생하는 HTTP 요청을 인터셉트 후, “filename” 부분을 확인하여

 

이를 ‘webshell.php.jpeg’에서 ‘webshell.php’로 다시 변경하고,

Forward를 해주면 정상적으로 업로드되는 것을 확인할 수 있다.

또 다른 방법으로는, “Content-Type” 부분을 확인할 수 있는데, 

위와 같이 이를 ‘application/x-php’에서 ‘image/jpeg’로 변경하고 Forward를 해줄 수 있다.

 

실습(High Level)
High Level 또한 Medium Level과 동일하게 JPEG 또는 PNG 파일만이 업로드 가능하다는 메시지를 출력한다. 

High Level의 소스코드를 살펴보면, 대소문자 구분 없이 파일의 확장자가 jpg, jpeg, png인 경우에만 파일 업로드를 허용한다. 

또 하나의 조건으로, getimagesize() 함수가 FALSE가 아니어야 한다. 

실제 이미지 파일이 아닌 경우에는 getimagesize()의 반환값이 FALSE이다.

 

이를 우회하는 방법은 Medium Level과 동일하게 파일의 확장자를 변경할 수 있다. 

하지만, 이 방법은 확장자만 변경한다고 이미지 파일이 되는 것이 아니다.

즉, 파일 Signature(GIF89a)를 추가하여, getimagesize() 함수를 우회하고

webshell.php.jpeg 파일을 필터링할 때 이미지 파일이라고 인식시켜 주면, 아래와 같이 업로드가 성공된다.

 

하지만, 현재 업로드된 파일명이 결국엔 ’webshell.php.jpeg’이기 때문에, URL에 입력 시 웹 셀을 실행할 수 없다. 

 

이를 우회하기 위해서 High Level에서는 파일 업로드 취약점뿐만 아니라 File inclusion 취약점 또한 존재하기 때문에,

해당 취약점을 이용하여 업로드된 webshell.php.jpeg의 내용을 직접 웹 페이지에 삽입해 준다.

그러면 위와 같이 의도했던 php 파일의 기능을 아래와 같이 실행시킬 수 있다.

 

 

즉, 웹 애플리케이션은 특정 공격 기법만을 방어한다고 해서 온전하게 대응할 수 있는 것이 아니다. 

 

LFI가 매우 드물게 나타나는 취약점이긴 하지만 어떤 형태로든 파일을 업로드하는 기능이 있으면,

위와 같이 취약점을 활용하여 공격을 시도할 수 있는 것이다.

실습(Impossible Level)

Impossible Level 또한 동일한 메시지를 출력하며, 소스코드를 살펴보았을 때, 이미지 파일인지 판별하는 것뿐만 아니라 imagecreatefromjpeg() 함수와 imagecreatefrompng() 함수를 이용하여 새로운 이미지로 만들고 있다. 

해당 함수는 이미지만을 처리하기 때문에 이미지 데이터가 아닌 부분은 모두 버린다. 

즉, 확장자만 jpeg였던 php 파일은 본래 의도된 역할을 할 수 없는 것이다.

 

대응방안

  1. 웹 서버 설정을 변경하여 파일이 저장되는 경로에서 업로드된 파일의 실행 권한을 차단한다. 
  2. 업로드 대상 파일의 확장자를 검증하는 처리 프로그램을 통해 서버 사이드 스크립트 파일의 업로드를 차단한다.

  3. 우회 기법을 통한 파일 업로드를 제한하기 위해서 파일 업로드 기능 여부를 점검하는 기능을 구현하여 사전에 차단한다.
      
  4. 파일이 업로드되는 디렉터리(위치 및 파일명)가 사용자에게 노출되지 않도록 조치한다. 

  5. 웹 서버와 파일이 업로드되는 서버를 물리적으로 분리한다.


 

 

Insecure CAPTCHA

 

 

개념
CAPTCHA란 웹사이트에서 자동화된 봇 트래픽을 차단하고, 사용자가 사람임을 검증하기 위해 사용된다. 

해당 기능은 Brute Force와 같이 컴퓨터에 의해 자동화된 공격에 대응이 가능하다.

 

해당 기능은 사실은 정확도가 높지 않다 보니까 오히려 효율성을 해치는 상황이 많이 발생되는 것을 느낄 것이다.

그래서 인증을 강화하기 위한 목적이기 보다는 단순히 사용자인지 봇인지 구분하기 위한 목적으로서 사용을 한다고 보면된다.

 

그래서 보안 감사 등에서 CAPTCHA는 추가적인 인증 수단으로는 약하다고 평가를 한다.

즉, 보통은 OTP, 생체인증과 같은 추가적인 2차 인증 수단을 통해 대처를 해놓아야 미흡하지 않고 '양호하다'라고 평가를 한다.

 

이러한 CAPTCHA 시스템이 불충분하거나 잘못 구현되어 공격자가 CAPTCHA를 쉽게 우회하거나,

자동화된 스크립트로 반복적으로 통과할 수 있는 경우 발생하는 보안 취약점을 Insecure CAPTCHA라고 말한다.

 

실습(Low Level)

Insecure CAPTCHA 실습 페이지에 접속하면 아래와 같이 새로운 패스워드를 입력하고 변경할 수 있는 화면을 확인할 수 있다.

또한, 해당 실습 페이지에서 CAPTCHA 인증을 완료하고 ‘change'

버튼을 클릭하면, 아래와 같이 또 다른 페이지로 넘어가게 된다.

해당 페이지는 “You passed the CAPTCHA! Click the button to confirm your changes.”

라는 문장을 출력하면서, 패스워드 변경을 수행할 의사를 다시 한번 묻는 것을 확인할 수 있다.

 

이러한 웹 페이지 구성은 상당히 취약한 점이 존재한다. 

해당 Low Level과 같이 사실상 첫 번째 페이지에서만 CAPTCHA를 사용하여 사용자가 봇인지 확인한다면, 

 

두 번째 단계부터는 CAPTCHA가 의미가 없어지고 추가적인 보안 검증이 없다는 것을 의미한다.
이는 결국 공격자가 첫 번째 CAPTCHA를 통과한 후 자신이 원하는 요청을 반복적으로 보내어 자동화된 공격을 수행할 수 있게 된다.

 

첫 번째 페이지에서의 HTTP 요청
두 번째 페이지에서의 HTTP 요청


즉, 두 번째 페이지의 요청을 인터셉트 후 Repeater로 보내어 요청을 조작한 후,

 

언제든지 해당 요청을 이용하여 첫 번째 페이지에서 CAPTCHA 기능이 구현이 되어있든 말든

이미 조작한 요청을 Request 하여 우회할 수 있는 것이다.

원래 ‘1234’로 변경을 했었지만 Repeater로 보낸 두 번째 페이지의 요청을 다시 활용하여,

 

‘password_new’와 ‘password_conf’ 부분을 다시 ‘password’로 변경하면,

Render 탭에서 패스워드가 다시 변경된 것을 확인할 수 있다.

 

실습(Medium Level)
Medium Level에서의 차이점은 ”passed_captcha” 파라미터가 추가되었다는 점이다. 

하지만 해당 Level 또한 페이지가 두 단계로 이루어져 있기 때문에 Low Level과 동일한 방법으로 우회가 가능하다.


실습(High Level)
High Level에서는 이전 두 단계와는 달리 CAPTCHA 인증 페이지 한 단계로 구성되어 있다. 

또한, HTTP 요청을 확인해 보았을 때, 이전에는 보지 못했던 “g-recaptcha-response”, “user_token” 등이 새롭게 보인다.


페이지의 소스코드를 살펴보는 과정에서 특이한 주석을 발견할 수 있다. 

“DEV NOTE”, “Response: 'hidd3n_valu3’ && User-Agent: 'reCAPTCHA’”라는 키워드로 보아 

개발자가 편의를 위해 작성해 둔 주석으로 보이며, 개발 완료 후 제거하지 않은 것으로 유추할 수 있다.


그렇다면 Burp Suite에서 확인한 요청과 비교 시,

‘Response’라는 단어와 가장 유사한 ‘g-recaptcha-response=03AFcWe…’, 

’User-Agent’라는 단어와 같은 ‘User-Agent: Mozilla/5.0…’ 부분을 Burp Suite에서 주석의 내용으로 변경해 볼 수 있다.

위와 같이 패스워드 변경이 성공한 것을 확인할 수 있다.

 

해당 Level은 개발자가 패스워드 변경 로직을 테스트할 때마다 CAPTCHA를 통과해야 하는

번거로움을 해결하기 위해 작성해 놓았던 값을 이용하여 우회할 수 있었다.

 

 

High Level의 소스코드를 살펴보면, 원래 ‘g-recaptcha-response’ 값은 랜덤 하게 지정되는 값이지만, 

고정된 문자열인 ‘hidd3n_valu3’와 비교하는 것을 확인할 수 있다.

 

실습(Impossible Level)

Impossible Level에서는 타 Level들과는 다르게 현재 패스워드 또한 확인하는 것을 볼 수 있다. 

이는 무차별 대입 공격을 대응할 수 있을 것이다.

 

소스코드 확인 시 개발자의 실수는 해결되었고, CSRF 토큰 검증, 

POD를 이용한 SQL Injection 방지 등의 대응조치를 취한 것을 볼 수 있다.

 

대응방안

  1. 동적 CAPTCHA 생성 : CAPTCHA는 각 요청마다 새로운 값을 부여하며, 세션에 해당 값을 저장해 서버 측에서 검증한다.

  2. 서버 측 CAPTCHA 검증 : CAPTCHA 검증은 서버에서만 수행되도록 한다. 
    CAPTCHA의 유효성을 클라이언트 측에서만 검증할 경우, 공격자가 클라이언트 코드나 스크립트를 조작하여 우회할 수 있다.

    서버에서 CAPTCHA 값을 세션에 저장하고, 제출된 값을 세션에 저장된 값과 비교하여 검증한다.

  3. CSRF 방지 토큰 사용 : 각 요청에 대해 CSRF 방지 토큰을 적용하여, 
    공격자가 임의로 생성한 요청으로 CAPTCHA를 우회할 수 없도록 한다.

    CSRF 토큰은 각 사용자의 세션에 고유한 값을 생성하여, 이를 통한 인증을 요구함으로써, 봇 공격이나 재사용된 요청을 방지한다.

  4. 요청 빈도 및 IP 제한 : 재시도 횟수 제한을 두고, 동일한 IP에서 반복적인 요청이 발생할 경우 
    일시적으로 요청을 차단하여, 봇이 무차별 대입 공격을 시도할 수 없도록 한다.

 

 


 

 

다음 글에서는 SQL Injection, SQL Injection(Blind)을 주제로 실습을 진행할 예정이다.