大坑:Libc,一定要换!!!
不要怀疑题目附件libc的正确性,注意程序运行环境(本机)的libc版本,gdb调试的时候libc版本不一样会出大问题而且意识不到这点极难排查。具体流程如下:
- 确定版本:拿到libc附件,题目有描述的看描述确定版本,没有描述的拖进IDA在字符串窗口搜索“GNU”确定libc版本。
- 更换libc:如果与环境libc不一致(大概率不一致),去glibc-all-in-one下载对应的libc版本,然后使用
patchelf
更换程序的libc和链接器,方便调试,命令为patchelf --replace-needed libc.so.6 你要换的libc的硬路径 ./pwn
和patchelf --set-interpreter ld的硬路径 ./pwn
.
- gdb调试确定各种偏移、差值、地址
小坑:提权(感谢l3h招新赛)
getshell后发现flag root只读怎么办?有了libc和gadget,使用setuid(0)
进行root提权即可(作为一种尝试,如果程序没有suid的root权限可能会不行,nkctf的时候是试一下成了所以总结在这)。或者使用orw方法open然后read到stdout。不要只惦记着system('/bin/sh')
。
保护绕过:canary和aslr
canary目前的经验是进行泄露,题目可能会有可以利用的栈上格式化字符串漏洞对canary进行泄露。有这么几点:
-
一般地在同一个程序中canary是不会动态变化的,泄露出一次后canary就固定了
-
canary是\x00
打头,是为了零截断,但是没什么用(笑),有格式化字符串的话直接读数就行
-
注意canary的检验方式,直接去看汇编,看检验的方式是什么样的
-
aslr
动态调试
确认本地libc环境和远程一样或者是静态编译的文件,动调是个非常非常非常有用的办法。甚至可以盲打,就不分析在输入的地方测一下溢出点然后看栈上的内容确定偏移就行。
另外,对于go(或者什么别的),确定偏移了之后发现用不了的话,记得看看栈上是不是有别的东西被覆盖了导致在溢出点前调用的函数进去之后报错
以ciscn2024初赛 gostack为例,测到溢出之后不用看什么静态的,直接动调打印栈:
after send cyclic(0x100):
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
| 06:0030│-1d0 0xc000074d90 ◂— 0x1 07:0038│-1c8 0xc000074d98 ◂— 0x6161616261616161 ('aaaabaaa') 08:0040│-1c0 0xc000074da0 ◂— 0x6161616461616163 ('caaadaaa') 09:0048│-1b8 0xc000074da8 ◂— 0x6161616661616165 ('eaaafaaa') ...... 25:0128│-0d8 0xc000074e88 ◂— 'kaaclaacmaacnaac' 26:0130│-0d0 0xc000074e90 ◂— 'maacnaac' 27:0138│ rdx 0xc000074e98 —▸ 0xc00018c200 ◂— 0x6161616261616161 ('aaaabaaa') 28:0140│-0c0 0xc000074ea0 ◂— 0x100 29:0148│-0b8 0xc000074ea8 ◂— 0x0 2a:0150│-0b0 0xc000074eb0 ◂— 0x0 2b:0158│-0a8 0xc000074eb8 —▸ 0x4aa800 ◂— 0x10 2c:0160│-0a0 0xc000074ec0 —▸ 0x4df040 —▸ 0x4c15af ◂— 0x6f79207475706e49 ('Input yo') 2d:0168│-098 0xc000074ec8 —▸ 0xc000074d98 ◂— 0x6161616261616161 ('aaaabaaa') 2e:0170│-090 0xc000074ed0 ◂— 0x100 2f:0178│-088 0xc000074ed8 ◂— 0x100 30:0180│-080 0xc000074ee0 —▸ 0x4df4e8 —▸ 0x4aeb40 ◂— 0x10 <-sus addr? 31:0188│-078 0xc000074ee8 —▸ 0xc0001ae000 —▸ 0xc0001b0000 ◂— 0x0 32:0190│-070 0xc000074ef0 —▸ 0x4c5dd0 —▸ 0x49b080 ◂— 0x13d860f10663b49 <-excutable addr? 33:0198│-068 0xc000074ef8 ◂— 0x10000 34:01a0│-060 0xc000074f00 —▸ 0xc0001b8000 ◂— 0x6161616261616161 ('aaaabaaa') 35:01a8│-058 0xc000074f08 ◂— 0x100 36:01b0│-050 0xc000074f10 ◂— 0x1000 37:01b8│-048 0xc000074f18 —▸ 0xc0001b8000 ◂— 0x6161616261616161 ('aaaabaaa') 38:01c0│-040 0xc000074f20 ◂— 0x1000 39:01c8│-038 0xc000074f28 ◂— 0x1000 3a:01d0│-030 0xc000074f30 ◂— 0x101 3b:01d8│-028 0xc000074f38 ◂— 0x101 3c:01e0│-020 0xc000074f40 ◂— 0x0 ... ↓ 2 skipped 3f:01f8│-008 0xc000074f58 ◂— 0x1 40:0200│ rbp 0xc000074f60 —▸ 0xc000074f70 —▸ 0xc000074fd0 ◂— 0x0 41:0208│+008 0xc000074f68 —▸ 0x4a0af2 ◂— 0x53058b481074c084 <- ret addr 42:0210│+010 0xc000074f70 —▸ 0xc000074fd0 ◂— 0x0
|
把写着地址的几个地方覆盖了(其实原因就是下一次fmt.Println的函数参数传0x616161肯定是不行),能ret2text的话其实直接原封不动地址抄到payload里都行
他妈的windows,赤石去吧
保护全开的程序也能打下来了,真爽吧。