2021 强网杯PWN部分WP
Last Update:
Word Count:
Read Time:
2021 强网杯PWN 部分WP
最终官方排名:23
baby_diary
漏洞点
1 |
|
执行完编辑函数,可以修改最后一个字节的low_byte位
1 |
|
然而sub_146E函数根据下面算法计算的,若大于0x0f的话,就不能构造结果为0,这有点坑。
1 |
|
漏洞综述,最后字节用’\x00’截断,4bit位溢出。
glibc 2.31下绕过unlink,稍微有点难构造,加上本身程序逻辑,更难构造了,各种层层构造关联太强了,但最后还是找的了某些地址,成功构造利用链子,这需要控制很好的地址的值,比如实现unlink时,prev_size 要满足 0x100的倍数,不然不好设置我们unlink chunk size低3位为 0,还有构造unlink的fd->bk 指向自己本身,bk->fd指向自己本身,然而程序有点烦人的是最后一字节为’\x00’截断的,后面有4bit位溢出,这使得我们伪造chunk的fd必需要为0x100的整数倍才行。实现unlink之后就实现了堆重叠,泄漏Libc然后再修改__free_hook
为system函数,至于glibc 2.31下如何绕过unlink,它与2.29一样的,多了个 prev_size == chunk_size的检查,这就比较麻烦, 可以参考这篇博客:https://bbs.pediy.com/thread-257901-1.htm 。
下面是我重重构造,实现unlink的信息
1 |
|
exp
1 |
|
noout
没有打印函数,通过’\x00’字节绕过字符串比较
1 |
|
再利用计算错误抛出SIGFPE信号使调用漏洞函数
1 |
|
1 |
|
漏洞函数中就是简单的堆栈溢出了,采用dl_runtime_resolve攻击。
exp
1 |
|
orw
一个伪heap题,开启了沙箱,编辑和打印功能没有,只能开辟两次堆,释放一次,没办法进行堆操作。
存在个index 负数溢出,可以实现修改got表,为堆地址。
1 |
|
查看程序架构
1 |
|
checksec发现存在rwx段,但发现是stack上的,想了半天没想通如何跳到堆栈那里去。
试试在堆上写shellcode,然后index溢出漏洞修改atoi的got地址为shellcode堆地址,跳到堆中执行指令,然而发现远程能执行,但自己本地不行,接下来就是orw的汇编指令编写了。
exp
1 |
|
shellcode
沙箱检查如下
1 |
|
输入的shellcode有检查
1 |
|
也就是机器码字符小于等于’\x31’的就退出或等于’\x7f’,我们可以采用alpha3工具将机器码生成可显示字符,当然这个工具有限制,机器码不能出现’\x00’,通过调试发现,shellcode的基址存放在rbx上,我们先实现一个输入的shellcode,避免后续不会再进行shellcode过滤。
1 |
|
在原来的shellcode + 0x120处实现输入,再跳到那个地方去。
采用alpha3工具生成可显示shellcode如下
1 |
|
当然我也写了个函数方便修改。
1 |
|
然而这个题禁用函数太多了,open和write也禁了,只能切换到32位架构来实现部分绕过了,为了方便实现堆栈,指令储存,我重新申请了个地址段,方便后续实现架构切换方便与数据写入等。
1 |
|
上面是实现向我们开辟到的内存写入数据,再从64位架构切换到32为且跳到我们开辟的内存段中。
后面就是写32位的asm code了,然而我发现,在32位下,只有一个有用的函数能调用,就是open函数,其他的read,write这些都不能调用了,这又使得重新回到64位下实现读入flag。
1 |
|
由于不能使用write的系统调用,只能采用延时爆破了
1 |
|
idx为读入的字符偏移,ch是我们猜测的字符,若想等,就进入死循环,否则就退出。
通过时间来判断是否想等。
总结:
自己踩了很多坑,shellcode必须为可显字符,后面绕过了,只能用少量的系统函数,64位架构时,只能使用read, mmap, fstat,我还以为切换架构到32位可以绕过syscall检测,想不到只允许调用open, 其他的read和write都不行,又重新切换到64位来执行read,再采用延时爆破读出来。
exp
1 |
|
pipeline
没有free函数,通过设置大小为0即可实现释放内存功能。
找了偏移,chunk头部链表逻辑,没有发现漏洞,在编辑数据的功能中,发现了个整型溢出漏洞。
漏洞点
1 |
|
一个整性溢出,因为采用LOWORD(size) = v3;
进行赋值的,当我输入负数绕过判断,若LOWORD(v3)中的值为大于size本身值,即可实现溢出,那么就很好利用了。实现了任意地址写入,但有个检查
1 |
|
而check_mem_buf的值在初始化的时候赋予了
1 |
|
基本上我们只能在堆段中实现任意地址写入了,这也比较好绕过,每个编辑功能都有个head chunk,修改head中的body指针,就可以实现任意地址写入数据了。
修改__realloc_hook
为system,再调用realloc函数即可调用system。
exp
1 |
|
babypwn
这个题也是个坑,打印函数采用加密
1 |
|
采用z3库来解,只能解一次循环加密的,第二次循环的解不出来,只能不用该条件了。
漏洞点
1 |
|
在输入完数据后,会死循环读取数据,若出现’\x00’则跳出,若出现’\x11’修改该字节为’\x00’且跳出循环。这利用方式就跟off by one差不多了。
程序开了沙箱
1 |
|
前期我以为程序是在2.31下的利用方式,我一直在glibc 为2.31的环境下调试,怎么都不好构造绕过prev_size == chunk_size这个检查,查看libc.so.6,发现为2.27的。。。
那就很方便的采用unlink构造堆重叠,由于没有办法解密上面那个泄漏的数据,只能partial write打到_IO_2_1_stdout
泄漏libc,打通几率1 / 16,
泄漏之后,然后劫持__free_hook
为setcontext + 53处的gadget实现堆栈迁移值 __free_hook - 0x108
处,这里我是放在__free_hook
高地址位置的,本地能打通,远程死活打不通,我只调用write函数能够泄漏地址信息,应该是某些部分数据被覆盖,导致我的rop链破坏了,只能将rop放在__free_hook
上面。
exp
1 |
|