1. Enueration

: Port Scanning

 

 

 

# gunicorn 20.0.4

 

: Gunicorn은 WSGI(Web Server Gateway Interface)

애플리케이션을 위한 순수 Python HTTP 서버로

해당 버전에는 HTTP request smuggling이라는

알려진 취약점이 있다.

 

우선 requset smuggling과 관련한 자세한 내용은

https://portswigger.net/web-security/request-smuggling

을 참조하고 대강 이해한 것을 정리해 보면 다음과 같다.

 

smuggling이라는 단어의 뜻은

마약을 밀수하다 할 때의 그 밀수라는 뜻이고

어떤 이유로 기존 HTTP request의 body에

또 다른 HTTP ruquest를 밀반입해 요청 시

해당 요청이 정상 처리되는 취약점이라 할 수 있는데

 

gunicorn 20.0.4 버전의 경우,

request header 부분의 Sec-Websocket-Key1

(Sec-Websocket-Protocol에서 서버의

handshake를 위한 정보를 계산하기 위해 필요한 값)

 

이라는 헤더를 분석하는 과정에서

이러한 HTTP request smuggling

취약점이 발생하게 된다.

 

원인은

/gunicorn/http/message.py 파일에는

들어오는 요청의 헤더(request header)를 기반으로

요청 본문의 크기를 결정하고 이를 읽어주는

reader를 생성하는 set_body_reader라는 함수가

있는데

 

만약 들어오는 요청(request)에

Sec-Websocket-Key1헤더가 포함되어 있으면

Content-Length 크기가 얼마든

요청 내 콘텐츠(body) 길이를 8 byte로

간주하기 때문.

 

 

여기 다음과 같은 요청이 있다.

GET / HTTP/1.1
Host: takudaddy.com
Content-Length: 48
Sec-Websocket-Key1: x

xxxxxxxxGET /admin HTTP/1.1
Host: takudaddy.com
 
 

프록시는 Content-Length 헤더를 보고

body 부분 내 모든 데이터를 포함하는 요청을

전달하는데

 

해당 요청이 gunicorn에 도달하면

gunicorn은 Sec-Websocket-Key1 헤더를 보고

Body 부분 데이터 중 8 byte 크기인

xxxxxxxx만 우선 읽어들인다.

 

이때 프록시와 gnicorn은

HTTP-keep-alive 상태이므로

gunicorn은 xxxxxxxx 다음에 이어지는 데이터들을

동일한 TCP connection 상의 다음 요청(next request)로

읽어드리고

 

이렇게 데이터를 가장한 request가

밀수되어 처리되는 것 같은 형태 때문에

HTTP request smuggling이라는

이름이 붙여지게 된 것 같다.

 

해당 취약점을 악용하면

접근 권한이 없는 특정 경로에

access가 가능해지고 중요한

데이터를 읽어올 수 있는 등의

행위가 가능하다.

 

 

 

:web enum

 

 

> 원래는 외부 네트워크에 노출되면 안 되는

중요한 API 란다.

 

 

본문 내용 중 힌트가 되는 부분을 살펴보면

 

update URL이

POST 방식으로

JSON 데이터를 요청 시

/update 엔드포인트에

해당 요청을 전달할 수 있다고 한다.

 

 

또한

 

 

/logs 엔드포인트에 접근하면

로그 파일들을 읽을 수 있다는데

접속해 보면 다음과 같은 메시지가 뜨며

 

웹 애플리케이션의 방화벽에 의해

접근이 제한되는 것을 확인할 수 있다.

 

 

 

 

 

2. Exploitation

우선 /update 엔드포인트는

당장 사용자 명을 알 방도가 없으니

건너뛰고 /logs 엔드포인트를 파본다.

 

 

2-1) Bypass WAF

WAF 에러 메시지 내용대로

 

 

외부 네트워크로 인식되는

지금 호스트로는 접근 권한이

없는데

 

전에 풀었던 문제(15. Robust)에서

사용했던 해결 방법 중 하나인

 

XFF (X-Forwarded-For)

: 서버에 요청한 사용자의 IP를 식별하기 위한 표준 기능!

 

헤더를 추가해 요청하면

WAF 우회가 가능하고

 

에러 메시지 내용을 살펴보면

file 파라미터를 통해

로그파일 경로를 지정하라고 하니

LFI가 가능하다고 추정되며

 

/update 경로를 공략하기 위해서는

사용자 이름 확인이 필요하기 때문에

passwd 파일을 불러오면

정상 조회 가능!

 

 

2-2) Update /update

 

업데이트를 위해

사용자 이름, update를 위해

받아올 파일의 경로가 필요한데

 

root 외의 사용자는 하나뿐이고

이름을 확인했으니 업로드 실험을 해볼 수 있고

 

파이썬 웹 서버를 기동해 주고

아무 파일이나 호출해 보면

정상 업로드가 가능하다.

 

 

쉘 생성 후

 

 

업데이트

 

> restart 하란다

 

 

 

리스너 기동 후

/restart 요청하면

 

안되는데

소스를 보면

 

 

재시작 요청은 GET 방식이 아니라

POST 방식으로 요청해야 한단다.

 

반영하여 재 요청하면

성공적으로 재시작 되면서

침투 성공!

 

 

3. Privilege Escalation

 

wget에 SUID가 걸려있고

새로운 사용자를 passwd 파일에

생성 후 받아 주면 되겠다.

 

 

암호 생성을 위해 주로 사용하는 방법은

크게 두 가지로

1. python
python -c 'import crypt;print(crypt.crypt("taku","taku"))'   
ta0LWDW4m3OdU (password=taku)

---------------------------------------------------------
2. openssl
# openssl passwd taku
cF3uSulrnlYNs (password=taku)

(salt 키 지정해 생성하는 경우)
# openssl passwd -1 -salt takudaddy taku
$1$takudadd$KETef9oIkYFX0zLAs6XjM. (password=taku)
 

 

아무거나 사용해 암호를 생성해준 뒤

passwd 파일 형식에 맞춰 passwd 파일로

저장해주고

 

 

파이썬으로 웹서버 기동한 뒤

wget으로 /etc/passwd 에 저장해

확인해보면

 

 

passwd 파일에는

변조한 사용자 하나만

들어가 있고

 

해당 사용자로 로그인 하면

끝!

 

 

 

 

 

 

 

728x90
반응형

+ Recent posts