HTTP Request Smuggling
주말에 팀원분들과 함께 HITCON CTF에 참여했다
Login System이라는 web문제를 시도했지만 못풀었다 ㅠ
WriteUp
https://github.com/maple3142/My-CTF-Challenges/tree/master/HITCON%20CTF%202023/Login%20System
대회가 끝나고 문제를 만드신 분의 write-up을 보고 알게된 내용을 정리하고자 한다
0xOne
님의 PPT자료도 참고해서 정리했다
우선 웹 기능부터 살펴보자
유저를 등록하고 로그인하면 profile을 보여주는 정말 단순한 웹서버이다
Reverse Proxy Server 또는 Load Balance 사용 시
CL(Content Length)와 TE(Transfer-Encoding)의
처리를 프론트엔드와 백엔드가 다르게 한다는 점을 이용한 공격
Proxy : 일반적인 Proxy 서버, 클라이언트의 요청을 가로채서 서버에게 보내고
서버에게 받은 Response를 다시 클라이언트에게 반환
Reversed Proxy : 웹 서버로 오는 요청을 가로채는 Proxy 서버, 주로 Load Balancing에 활용
서버의 IP를 노출시킬 필요가 없어짐, 성능 향상을 위한 캐시 데이터 저장 가능
(Load balancing) : 서버가 처리해야할 업무를 분산처리
Reverse Proxy Server 또는 Load Balance 사용 시
CL(Content Length)와 TE(Transfer-Encoding)의 처리를
프론트엔드와 백엔드가 다르게 한다는 점을 이용한 공격
Content Length
는 말 그대로 body의 총 길이를 인식
Transfer Encoding
은 컨텐츠 인코딩 방식을 결정
chunked는 데이터를 분산 청크로 전송, 각 Chunk는 Length와 Data로 구분
아래는 chunked가 데이터를 처리하는 방법을 나타낸 사진이다
body의 데이터 길이를 계산할 필요가 없으며, 각각의 chunk마다 길이가 주어지고 0의 길이를 받으면 패킷이 끝나는 걸로 확인한다
이처럼 0으로 판단하냐, 제공된 길이를 통해 판단하느냐의 차이로 CL.TE
, TE.CL
이 가능하게 된다
Frontend에서 Cotent Length를, Backend에서 Transfer-Encoding을 이용하여 패킷 길이를 계산하는 경우 Exploit
CL
은 SMUGGLED 까지 패킷으로 인식
TE
는 0 까지 패킷으로 인식
즉, 프론트엔드에서는 SMUGGLED까지를 패킷으로 받아들여 통과했지만, 백엔드에서 SMUGGLED는 새로운 패킷의 시작점으로 인식하게 된다
Frontend에서 Transfer-Encoding를, Backend에서 Content-Length을 이용하여 패킷 길이를 계산하는 경우 Exploit
TE
는 0 까지 패킷으로 인식
CL
은 8 까지 패킷으로 인식
즉, 프론트엔드에서는 0까지 패킷으로 받아들여 통과했지만, 백엔드에서 SMUGGLED는 새로운 패킷의 시작점으로 인식하게 된다
Frontend, Backend 모두 Transfer-Encoding를 통해 패킷 길이를 계산하는 경우
위 사진처럼 어떤 방식으로든 헤더를 난독화 하여, Front & Back 둘 중 하나가 헤더를 처리하지 않도록 함
HTTP Requests Smuggling에 대해서 알아보았으니 문제를 확인해보자
문제에서 사용된방법은 TE.TE
가 사용되었다
nim과 node.js모두 TE를 통해 패킷을 인식한다
nim
에서는 대문자 CHUNKED
를 처리하지 못하지만,
node.js
는 문제없이 읽어들이게 된다
writeup의 코드를 보면 POST /change_password HTTP/1.0...
을 data에 담아
HTTP smuggling을 작동하게 하고 비밀번호 변경을 권한없이도 실행하게 한다
그리고 username을 입력값 검증없이 파일명으로 그대로 사용하고 있다
username에 ../../
같은 pathtravelsal을 넣어도 문제가 없다는 뜻이다
사용자 이름을 조작하여 설정파일로 만든 후
새로운 계정을 조작된 설정파일을 사용하도록 한다면 원하는대로 권한을 사용할 수 있게 된다
writeup을 보면 위 방법을 사용해서 yaml파일을 생성 후
privilegeLevel을 js코드로 작성해 flag를 출력하도록 되어있다
yamluser = os.urandom(8).hex()
json_inject(
yamluser + ".yaml\0",
""""peko", "access": {"profile": true, "flag":true}, "privilegeLevel": { toString: !!js/function "function(){ console.log('pwned'); flag=process.mainModule.require('child_process').execSync('/readflag','utf-8').toString(); return flag }" } } # """,
priloc="after",
)
대회참여날 문제풀때는 단순히 /flag
엔드포인트에 접근하는걸 목표로 했지만
지금 다시 코드를 보니 flag를 hash처리해서 보여주기 때문에 flag를 알 수 가없다
그래서 단순히 flag권한을 부여하는게 아니라 privilegeLevel에 ejs가 실행가능한 코드를 넣어주는 것이다
결론적으로 exploit이 되는 과정을 정리하자면
username
에 .yaml\0
을 넣어 설정파일로 만들어지게 한다
exploit코드를 실행해보면 privilegeLevel에서 pathtravelsal이 잘 된걸 확인할 수 있다
web에서 확인한 flag이다
오 저도 요번에 HTTP Request Smuggling에 관한 CTF문제를 풀었지만 정확하게 이해는 되지 않았는데, 이 글보고 좀 더 확실해게 HTTP Request Smuggling에 대해 알게되었습니다.