第二届网鼎杯半决赛easypwn题目复盘从0到1

admin 2020年12月11日11:32:05评论230 views字数 2614阅读8分42秒阅读模式

1. 题目分析

只给了一个ELF文件。

运行:

第二届网鼎杯半决赛easypwn题目复盘从0到1

静态分析:

在main()中可以看到read()读取0x90个字节进入buf,这里没有发生栈溢出

第二届网鼎杯半决赛easypwn题目复盘从0到1

输入数据需要经过check(),check()的作用的检查输入的数据是否包含“(”, “)”, “{”, “}”, “]”, “[”,包含如上字符则返回0。

第二届网鼎杯半决赛easypwn题目复盘从0到1

如果输入的数据中没有包含以上字符串,则调用write_f()。write_f()会将输入的数据写入“/pwn/ammm.c”

第二届网鼎杯半决赛easypwn题目复盘从0到1

写入后,start_exec()会调用gcc编译“/pwn/amm.c”并执行“/tmp/lizi”。

第二届网鼎杯半决赛easypwn题目复盘从0到1


2.  思路1

gcc编译一个后门,第一思路当然是直接调用“system(“/bin/sh”)”,显然有“(”,这是过不了check()的。


第二届网鼎杯半决赛easypwn题目复盘从0到1


3.  思路2

所以直接调用C函数是不行的,那就继续测试编译汇编。但通过测试”gcc /pwn/amm.c -o/tmp/lizi”编译汇编也是不行的。

以下面代码为例

void main(){       System(“/bin/sh”);}

gcc编译以上代码是肯定能编译通过的。

使用“gcc -S a.c”,将生成的汇编文件a.s的文件名修改a.c。然后“gcc a.c”会报如下错误。

第二届网鼎杯半决赛easypwn题目复盘从0到1

所以gcc是会判断文件后缀的。

第二届网鼎杯半决赛easypwn题目复盘从0到1


4.  思路3

在C语言中定义一个全局变量const long main=……编译后的程序就会运行这段机器码。但实现起来需要“亿”点细节。

首先long型只有8个字节,而我们想要执行的x64的shellcode有如下48个字节

"x6ax68x48xb8x2fx62x69x6ex2fx2fx2fx73x50x48x89xe7x68x72x69x01x01x81x34x24x01x01x01x01x31xf6x56x6ax08x5ex48x01xe6x56x48x89xe6x31xd2x6ax3bx58x0fx05"

这段shellcode来源于python:

from pwn import *context(os='linux',arch='amd64')shellcode = asm(shellcraft.sh())print shellcode.encode(“hex”)


第二届网鼎杯半决赛easypwn题目复盘从0到1

首先通过数组的形式将shellcode赋值给main,这个方式是可以运行这段shellcode的。但是代码中有“[”,显然这还不是我们想要的答案

第二届网鼎杯半决赛easypwn题目复盘从0到1

在IDA上看到main函数指针处看到了shellcode

第二届网鼎杯半决赛easypwn题目复盘从0到1

再测试以指针的方式赋值,main处获得的是一个指针,运行时会发生Segmentation fault。char *型改成long型也是得到的结果一样。

第二届网鼎杯半决赛easypwn题目复盘从0到1

IDA上看到main指针处的是一个指针0x400568

第二届网鼎杯半决赛easypwn题目复盘从0到1

而0x400568处是shellcode。

第二届网鼎杯半决赛easypwn题目复盘从0到1

定义main为一个long型,最多可以赋值8个字节大小整型给main,也就是说可以执行1或2个长指令,所以我们让main()函数执行“jmp”让执行流转跳到0x400568上,即让main()函数执行指令“jmp 0x400568”。这时候我们就需要找到“jmp 0x400568”的机器指令。

这里我们通过在main()函数内嵌汇编的方式找想要的机器指令,如下图:

第二届网鼎杯半决赛easypwn题目复盘从0到1

需要注意的是,这时候编译出来的程序”jmp 0x400568”还不能转跳到shellcode上,因为这时候main()上有多余的指令“push rbp”和”mov rbp,rsp”,导致shellcode在0x400578上,但是不着急,我们只要”jmp 0x400568“的机器码。

第二届网鼎杯半决赛easypwn题目复盘从0到1

如下图IDA显示“jmp loc_400568”在0x4004DA处

第二届网鼎杯半决赛easypwn题目复盘从0到1

在IDA的子窗口“Hex Vie-1”,看到“E9 89 00 00 00”就是机器码

第二届网鼎杯半决赛easypwn题目复盘从0到1

然后写成下面这样。貌似还没成功,从gdb上看到执行的指令是”jmp 0x40062e”

第二届网鼎杯半决赛easypwn题目复盘从0到1

但是我们想要的指令是”jmp 0x400568”,我们可以根据“jmp”指令的运算逻辑用“jmp 0x40062e”计算”jmp 0x400568”。已知”jmp 0x400568”的机器码是“E9 89 00 00 00”。然后计算0x40062e-5-0x89等于0x4005a0。0x4005a0正是”jmp 0x40062e”的指针。

所以得到一个算式:0x40062e(想要转跳的指针)-0x4005a0(执行指令的指针)-5=0x89。

然后算0x400568-0x4005a0-5得到-61,-61再转成补码是0xffffffc3。最后得到”jmp 0x400568”的机器码应该是“E9 c3 ff ff ff”,编译的C文件内容如下:

第二届网鼎杯半决赛easypwn题目复盘从0到1

最终得到的exp脚本如下:

第二届网鼎杯半决赛easypwn题目复盘从0到1

后来又试了下,也可以在IDA中直接修改指令,得到的机器码。在main(),0x4005a0处将原来的指令修改为“jmp     400568h;”

第二届网鼎杯半决赛easypwn题目复盘从0到1

得到机器指令”EB C6”

第二届网鼎杯半决赛easypwn题目复盘从0到1

修改exp,运行效果如下

第二届网鼎杯半决赛easypwn题目复盘从0到1


5.  修复漏洞

check()函数中“)”修改为“h”,可以检测输入数据中是否包含“/bin/sh”。

第二届网鼎杯半决赛easypwn题目复盘从0到1

 

6.  总结

按照这个思路来,一点也不easy。该思路灵感来源于某一位知乎。这里主要描述了这个思路实现细节。


第二届网鼎杯半决赛easypwn题目复盘从0到1

不同的环境下编译出的指针会不一样,但思路都是一样的。

听说有人通过#include“/flag”,能够打印出flag。但明显这是非预期解。检测字符“h”能防守成功,说明预期解就是要执行shellcode的。



山石网科安全技术研究院简称“山石安研院”,是山石网科的信息安全智库部门,主要负责反APT研究、出战及承办全球攻防赛事、高端攻防技术培训、全球中英文安全预警分析发布、各类软硬件漏洞挖掘和利用研究、承接国家网络安全相关课题、不定期发布年度或半年度的各类技术报告及公司整体攻防能力展现。技术方向包括移动安全、虚拟化安全、工控安全、物联安全、区块链安全、协议安全、源码安全、反APT及反窃密。


自2015年以来为多省公安厅提供技术支撑工作,为上合峰会、财富论坛、金砖五国等多次重大活动提供网络安保支撑工作。在多次攻防赛事中连获佳绩,网安中国行第一名,连续两届红帽杯冠军、网鼎杯线上第一名,在补天杯、极棒杯、全国多地的护网演习等也都获得优秀的成绩,每年获得大量的CNVD、CNNVD、CICSVD、CVE证书、编号和致谢。


如需帮助请咨询 [email protected]

第二届网鼎杯半决赛easypwn题目复盘从0到1

本文始发于微信公众号(山石网科安全技术研究院):第二届网鼎杯半决赛easypwn题目复盘从0到1

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2020年12月11日11:32:05
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   第二届网鼎杯半决赛easypwn题目复盘从0到1https://cn-sec.com/archives/200520.html

发表评论

匿名网友 填写信息