Professional Documents
Culture Documents
WriteUp IDSECCONF 2017
WriteUp IDSECCONF 2017
-- b e r d o e z t --
-- h a n u g r a --
Online 1
Misc 1
Dungeon 1
Reverse 3
Logang 3
Darwin 3
Whoami 8
Offline 13
Pwn 13
Proto 13
Money 15
Reverse 16
Nux 16
Misc 20
Low Bit 20
Binary :
https://drive.google.com/open?id=1PAa8JAkZ1hr2TIo0F4AxytOiDWg3S_nw
Online
Misc
Dungeon
Diberikan sebuah service di mana kita harus menyelesaikan sebuah maze. Kita harus
menentukan langkah dari ‘P’ agar dapat menuju ke ‘E’ dengan langkah yang optimum. Jika
terdeteksi tidak optimum, maka program akan langsung keluar. Dan juga, ada lebih dari 1
level yang harus kita selesaikan. Oleh karena itu, buat script sederhana seperti berikut.
#!/usr/bin/env python
from pwn import *
langkah = []
def cetak():
for i in range(len(grid)):
for j in range(len(grid[i])):
print grid[i][j],
print
print
if grid[x][y] == 'E':
print 'found at %d %d' % (x, y)
langkah.append(step)
return True
elif grid[x][y] == '+' or grid[x][y] == '-' or grid[x][y]
== '|':
return False
elif grid[x][y] == 'x':
return False
grid[x][y] = 'x'
langkah.append(step)
langkah.pop()
return False
a = remote('62.4.3.98', 10111)
a.recvuntil('Enter to start')
a.sendline('')
a.recvline()
a.recvline()
while 1:
rows = int(a.recvline().split(':')[1].strip())
cols = int(a.recvline().split(':')[1].strip())
dungeon = []
hasil = a.recvuntil('Enter your move:').split('\n')
pos = hasil[-2]
row_pos = int(pos.split(':')[1].split()[0].strip())
col_pos = int(pos.split(':')[2].strip())
for i in hasil[:-2]:
print i
search(row_pos, col_pos)
cetak()
langkah = langkah[1:]
for i in langkah:
a.sendline(i)
# print a.recvline()
a.recvuntil('continue.')
a.sendline('')
a.recv()
print a.recv()
print a.recvline()
langkah = []
Flag : flag{pOwer_pOLe_paPAh}
Reverse
Logang
Diberikan binary yg sepertinya dibuat dengan GoLang. Coba jalankan strings terlebih dahulu
untuk melihat apakah ada yang mencurigakan misalnya ‘flag’, ‘{‘, ‘}’. Didapatkan beberapa
string yang diduga flag.
‘flag{’
‘go_rEverse’
‘_aLa_pApah}’
Flag : flag{go_rEverse_aLa_pApah}
Darwin
Diberikan binary Mach-0 i386. Berikut hasil decompile dengan menggunakan IDA.
v4 = *(_DWORD *)__stack_chk_guard_ptr;
memset(v3, 0, 0x20u);
printf("Enter the key : ");
fflush((FILE *)*__stdoutp_ptr);
v2 = 0;
fgets(v3, 32, (FILE *)*__stdinp_ptr[0]);
while ( v2 < 32 )
{
v0 = 0;
if ( v3[v2] == 10 )
v0 = -10;
v3[v2++] += v0;
}
if ( sub_1A30((int)v3) )
printf("Congratz, the flag :\nflag{yOu_%s_3v3r}\n", v3);
else
printf("Wrong %s not the key.\n", v3);
result = 7566;
if ( *(_DWORD *)__stack_chk_guard_ptr == v4 )
result = 0;
return result;
}
Pengecekan terdapat pada fungsi sub_1A30(). Jika mengembalikan nilai yang benar, maka
kita akan mendapatkan flagnya yang sebagian sudah diketahui. Berikut hasil decompile
fungsi sub_1A30().
Karena fungsi tersebut harus bernilai 1, maka terdapat aturan - aturan sebagai berikut.
1. Panjang string 13
2. Huruf pertama = chr(51), huruf kelima = chr(53), huruf kedelapan = chr(52), huruf
kesepuluh = chr(55)
3. Penjumlahan index yang ganjil = 498
4. Penjumlahan index yang genap = 668
5. Penjumlahan index yang genap % 10 = 8
6. Penjumlahan index yang genap % 10 = Penjumlahan index yang ganjil % 10
7. Hasil XOR seluruh index yang genap = 98
8. Hasil XOR seluruh index yang ganjil = 56
9. Hasil XOR seluruh 6 index pertama = 21
10. Hasil dari fungsi sub_19c0 = 0xFD4E6A44
11. Seluruh index harus di antara 0 - 255.
Dengan memperhatikan aturan - aturan tersebut, kita dapat menggunakan library z3 agar
lebih mudah. Berikut script yang dibuat.
#!/usr/bin/env python
from z3 import *
import string
s = Solver()
num = [BitVec(i, 32) for i in range(13)]
#1
s.add(num[1] == 51)
s.add(num[5] == 53)
s.add(num[8] == 52)
s.add(num[10] == 55)
#2
s.add(num [1] + num [3] + num [5] + num [7] + num [9] + num [11]
== 498)
s.add(num [0] + num [2] + num [4] + num [6] + num [8] + num [10]
+ num [12] == 668)
s.add((num [0] + num [2] + num [4] + num [6] + num [8] + num [10]
+ num [12]) % 10 == 8)
s.add((num [0] + num [2] + num [4] + num [6] + num [8] + num [10]
+ num [12]) % 10 == (num [1] + num [3] + num [5] + num [7] + num
[9] + num [11]) % 10)
#3
s.add(num [0] ^ num [2] ^ num [4] ^ num [6] ^ num [8] ^ num [10]
^ num [12] == 98)
s.add(num [1] ^ num [3] ^ num [5] ^ num [7] ^ num [9] ^ num [11]
== 56)
s.add(num [0] ^ num [1] ^ num [2] ^ num [3] ^ num [4] ^ num [5]
== 21)
#4
coba = 23
for i in range(13):
coba = ( (num[i] << i) + 7 * coba) & 0xffffffff
coba = (coba >> 4 ) & 0xffffffff
s.add(coba == 0xFD4E6A44)
#5
for i in range(13):
s.add(num[i] >= 0)
s.add(num[i] <= 255)
print s.check()
print s.model()
➤ python darwin.py
sat
[k!12 = 74,
k!11 = 34,
k!9 = 142,
k!7 = 180,
k!6 = 30,
k!4 = 224,
k!3 = 38,
k!2 = 74,
k!0 = 159,
k!10 = 55,
k!8 = 52,
k!5 = 53,
k!1 = 51]
Flag : flag{yOu_r3ver5eM4s7er_3v3r}
Whoami
Diberikan sebuah binary Windows 32 bit. Berikut hasil decompile fungsi main. Ketika
program dijalankan, akan keluar sebuah pop - up yang menyatakan jika flag salah.
Secara sekilas, dapat dikatakan bahwa jika nama filenya benar, maka pop - up yang muncul
adalah flagnya. Dengan kata lain, kita harus mencari nama file yang benar.
Beberapa hal yang menarik adalah, terdapat pengisian beberapa konstanta pada fungsi
sub_403274() dan sub_4016e8(). Setelah googling, didapatkan bahwa untuk fungsi
sub_403274() merupakan pengisian nilai awal sha256(), sedangkan fungsi sub_4016e8()
merupakan pengisian nilai awal md5(). Oleh karena itu, dapat diduga juga fungsi yang
memiliki instruksi _ROR_ dan _ROL_ yang terdapat di dalam binary merupakan proses
hashingnya untuk kedua hash yang digunakan tersebut. Hal ini dapat dilihat dari
penggunaan konstanta - konstanta tersebut.
Dengan demikian, agar lebih mudah dibaca, kita dapat mengganti nama untuk beberapa
fungsi pada IDA.
Hasil XOR ini diduga haruslah selesai sampai dengan index 24 untuk mendapatkan
sha256() yang tepat.
Kita dapat langsung mendapatkan flagnya tanpa harus mengetahui nama filenya terlebih
dahulu. Hal ini dikarenakan poin 3 dan juga poin 2 dengan memanfaatkan asumsi bahwa
flag pasti diawali dengan ‘flag{’ dan diakhiri oleh ‘}’. Dengan adanya asumsi tersebut,
pengecekan kita akan mendapatkan 5 byte pertama dan 1 byte terakhir dari sha256(). Byte -
byte yang didapatkan tersebut sudah cukup untuk mendapatkan kembali byte - byte lain dari
sha256() sehingga kita dapat melakukan XOR dengan konstanta. Perlu diperhatikan, bahwa
pada byte ke 22 tidak dilakukan pengecekan sha256() dikarenakan memang for loop hanya
sampai index ke 24, sementara pengecekan byte ke 22 ada di index ke 25. Tapi tidak
masalah, kita dapat ‘menebaknya’ nanti. Berikut script sederhana yang coba dibuat.
#!/usr/bin/env python
# Pengecekan sha256()
a = [2,4,0,3,0x1f,1]
b = [0x13,8, 0x0E,
0x0B,0x18,0x0D,0x1B,0x11,5,0x14,0x17,0x1E,0x19,0x0C,0x1C,0x10,9,0
x1D,0x1A,0x0F,7,0x15,0x0A,0x12,6,0x16]
c = [0x0C9,0x0E0,0x0D8,0x9F,0x25,0x0FD,0x0AB,0x99,0x0C8,
0x1E,0x3A, 0x0B5, 0x5C, 0x4C, 0x0BB, 0x0B,0x1E,0x2D, 0x1C,0x3C,
0x22, 0x3E, 0x0B0, 0x0DC,0xD8]
flag = 'flag{'
for i in range(len(flag)):
sha256[i] = ord(flag[i]) ^ (konstanta[i])
hasil = ''
for i in range(len(sha256)):
if sha256[i] != []:
hasil += chr((sha256[i]) ^ (konstanta[i]))
else:
hasil += 'e' # my guess (bisa aja E atau 3)
print hasil
Perlu diketahui juga, tebakan huruf ‘e’ ini didapatkan dikarenakan melihat flagnya.
Sebenarnya bisa saja dicek dengan md5 untuk lebih pasti, namun tidak dilakukan.
Didapatkan hasilnya
➤ python siapa.py
flag{_Th3_F3a5t_4nd_7he_F4m1ne_}
Flag : flag{_Th3_F3a5t_4nd_7he_F4m1ne_}
Offline
Pwn
Proto
Diberikan sebuah binary 32 bit ELF yang dapat menerima request POST dan GET. Berikut
hasil decompile untuk fungsi terima().
Somehow, jika dicoba dikirimkan “POST” lalu dienter, akan muncul semua filenya dan
terdapat flagnya.
Money
Diberikan sebuah binary 32 bit ELF yang memiliki fungsi utama sebagai berikut.
int sub_8048811()
{
int v0; // eax@1
int result; // eax@2
__int16 v2; // [sp+1Eh] [bp-22h]@1
_BYTE v3[3]; // [sp+2Dh] [bp-13h]@1
int v4; // [sp+3Ch] [bp-4h]@1
sub_80487FD();
v0 = std::operator<<<std::char_traits<char>>(&std::cout, "Give
money address!! : \n");
std::ostream::operator<<(v0, (const char
*)&std::endl<char,std::char_traits<char>>);
std::operator>><char,std::char_traits<char>>(&std::cin, &v2);
v4 = 0;
strcpy(v3, (const char *)&v2);
puts(" Best Money 0x70fee061");
printf(" Your Money 0x%08x\n", v4);
if ( v4 == 0x70FEE061 )
{
puts("\n The Gold");
system("cat flag.txt");
result = 0;
}
else
{
puts("\n Bad Money");
result = 0;
}
return result;
}
Terlihat bahwa jika v4 bernilai 0x70fee061, maka akan didapatkan flagnya. V4 berada di
bawah v3 yang merupakan hasil pengcopian dari v2 yang adalah inputan kita. Jarak antara
v4 dan v3 adalah 15. Sehingga kita bisa memberikan payload seperti berikut untuk
mendapatkan flag.
Reverse
Nux
Diberikan sebuah binary 64 bit ELF yang dipack menggunakan UPX. Unpack terlebih dahulu
agar mudah untuk dianalisa. Jika program tersebut dijalankan, didapatkan seperti berikut.
➤ ./nux
[+] Initializing Engine Version 5.8.283.38
[+] Please type 2FA to start the engine : 123
[-] Failed to authenticate !
[+] Please type 2FA to start the engine : 123
[-] Failed to authenticate !
[+] Please type 2FA to start the engine : 123
[-] Failed to authenticate !
[-] Our last generated 2FA is 848628
[-] Immortal Joe : *aargh... Mediocre..!!
[-] Crashing...
Setelah distrings, didapatkan ada sebuah string ‘[+] Congratulations : flag{‘ pada binary.
Sepertinya ini lah tujuan kita. Namun jika kita decompile, banyak sekali pengecekan yang
dilkakuan. Selain itu, banyak juga ‘sampah’ yang sepertinya IDA salah menginterpretasinya.
Hal ini menyebabkan analisa menjadi semakin sulit.
Namun, pada bagian awal program dijalankan, terdapat bagian yang cukup menarik.
v29 = time(0LL);
srand(v29);
v30 = v148;
v31 = rand();
LODWORD(v32) = sub_43C510(v30, 32 * (v31 - ((((unsigned
int)(v31 >> 31) >> 24) + v31) & 0x7FFFF00)));
v114 = v32;
v320 = v32;
v319 = &v115;
v321 = &v320;
v115 = v32;
Sepertinya bagian tersebut membuat sebuah bilangan acak dengan seed time. Artinya
setiap detik, akan berbeda - beda hasil nilai randomnya.
Karena membaca hasil decompile sangat sulit, maka dicoba dilakukan blackbox testing.
Pada program, jika kita masukkan berapa pun, maka akan muncul tulisan ‘Failed’. Namun
yang membedakan adalah di akhir program akan muncul sebuah nilai, misal seperti contoh
di atas yaitu 848628. Mungkin ada hubungannya dengan nilai random yang digenerate di
atas. Dicoba dijalankan berkali - kali program tersebut dengan script berikut.
!/usr/bin/env python
from pwn import *
while 1:
a = process('./nux')
print a.recv()
for i in range(3):
a.sendline('1')
print a.recv()
a.close()
➤ python nux.py
[+] Starting local process './nux': pid 1115
[+] Initializing Engine Version 5.8.283.38
[+] Please type 2FA to start the engine :
[-] Failed to authenticate !
[+] Please type 2FA to start the engine :
[-] Failed to authenticate !
[+] Please type 2FA to start the engine :
[-] Failed to authenticate !
[-] Our last generated 2FA is 155558
[-] Immortal Joe : *aargh... Mediocre..!!
[-] Crashing...
Dari percobaan di atas, terlihat bahwa waktu yang sama yaitu untuk detik yang sama,
program menghasilkan nilai 2FA yang sama. Dan sepertinya, memang kita harus menebak
nilai 2FA ini karena di awal diminta untuk memasukkan nilai 2FA. Jadi kita hanya perlu
menebak nilai 2FA ini untuk mendapatkan flagnya. Namun, nilai random tersebut muncul di
akhir. Sementara, kita harus menebaknya di awal.
Cara yang paling mudah yang terpikirkan adalah dengan menggunakan threading.
Dikarenakan program akan menghasilkan nilai random yang sama pada waktu yang
bersamaan, maka jika kita memiliki 2 program yang berjalan bersamaan, tentu nilai
randomnya akan sama. Program pertama akan dijalankan sampai selesai sehingga
didapatkan nilai randomnya. Lalu nilai random ini akan dimasukkan ke program yang kedua
sebanyak 3 kali. Asumsi ini juga semakin diperkuat dengan hint dari panitia yang
menyebutkan ‘Javascript’. Mungkin saja akan didapatkan flag. Mari kita coba dengan script
sederhana berikut.
#!/usr/bin/env python
temp = -1
def b(x):
global temp
a = process('./nux')
if x:
while temp == -1:
continue
a.recv()
for i in range(3):
a.sendline(temp)
print a.recv()
else:
for i in range(2):
a.sendline('1')
a.recv()
a.sendline('1')
hasil = a.recv()
# global temp
temp = hasil.split('\n')[1].split('is')[1].strip()
thread.start_new_thread(b, (0,))
thread.start_new_thread(b, (1,))
while 1:
pass
Dicoba dijalankan
➤ python nux.py
[+] Starting local process './nux': pid 2921
[+] Starting local process './nux': pid 2923
[-] Failed to authenticate !
[+] Please type 2FA to start the engine :
[-] Failed to authenticate !
[+] Please type 2FA to start the engine :
[+] Congratulations : flag{3t3rnal_Sh1nY_&_Chr0m3}
Misc
Low Bit
DIberikan sebuah file main.ino.hex.zip, setelah di extract dan ditemukan file main.ino.hex
:100000000C943E000C945B000C945B000C945B0021
:100010000C945B000C945B000C945B000C945B00F4
:100020000C945B000C945B000C945B000C945B00E4
:100030000C945B000C945B000C945B000C945B00D4
:100040000C945B000C945B000C945B000C945B00C4
:100050000C945B000C945B000C945B000C945B00B4
:100060000C945B000C945B000C945B000C945B00A4
:100070000C945B000C945B000C945B0011241FBE7D
…
…
...
1
https://en.wikipedia.org/wiki/Intel_HEX
ditemukan bahwa file tidak jelas isi dan file signaturenya
Coba lakukan bruteforce XOR per byte file dengan 1 byte integer hingga ditemukan sebuah
string flag
Solver.py
#!/usr/bin/env python
test = []
string_flag = ''
j = 0
:
while 1
for i i n test:
string_flag += chr(ord(i)^j)
if "flag" in string_flag:
print string_flag
print "XOR dengan ",j
break
else:
j+=1
# flag{7h3re_15_N0th1ng_l3f7_70_l0s3}