周报
type
status
date
slug
summary
tags
category
icon
password
AI summary
0x01
进度
做了一下春秋杯的题,发现还是有些新东西的,学会了orw之外的沙箱绕过,还有自己手写了一个单字节爆破的脚本,也算是锻炼了下我的编程水平
之外就是做了下buu的前面几道逆向题,学会了ida的一些快捷键,练习了ida动态调试,记忆最深刻的是迷宫题(当时还不知道叫这个名字),就一步步的硬调,最后调出来了,相当有成就感
还有就是学习了arm汇编,主要是跟着azeria-labs,熟悉了arm的寄存器,掌握了str和ldr两条相当重要的对内存操作的汇编以及初步学习了它的寻址方式(现在就看到立即数寻址)
思考
感觉自己逆向水平不足,需要锻炼逆向水平,现在的想法就是做一些逆向题来提高逆向水平
还有就是打一些国际赛,拓展一下见识,看看pwn题有什么新活
剩下的部分我的想法是学完arm汇编后,做一些arm-pwn巩固一下(发现了一个网站ropemporium,挺好的)
0x02
记录
复现一下cve,首先在官网下载固件
Snipaste_2024-07-19_18-22-56
查看下文件类型是u-boot
u-boot
使用binwalk提取文件系统
M
参数,递归扫描文件
e
参数,提取固件中的文件
安装binwalk(推荐)
如非必要不要选择下面的安装方式(笔者曾尝试过,这样会删除很多依赖,辛辛苦苦搭起来ubuntu环境全烂了)
提取完后如图所示
bin
接下来我们将
goahead
的环境搭起来,goahead
在bin目录下qemu启动goahead
ld
看报错,缺少lib目录下ld,我们在当前目录下创建lib并将ld放在文件夹下即可
有很多缺少库的报错,我们根据报错将库放入即可
最后结构如图所示
tree
qemu运行,然后不出意外的又失败了
qemu
可以看见goahead报错cannot open pid file,我们就去程序里找
ida搜索字符串
string
是无法fopen
off_480e50
open
发现是没有/var/run/goahead.pid,创建一个
pid
又报错please execute nvram_daemon first!
nvram
发现缺失/var/run/nvramd.pid
nvramd
报错failed to convert to binary ip data
data
定位到lan_ipaddr
lan
卡住…
看见网上的办法都是ida远程调试hook掉,待我研究研究qemu远程调试
疑问
如何确定是哪个二进制文件?我看网上全是直接开始分析goahead,但是在漏洞挖掘中我们是不知道哪里有漏洞的,如何确定漏洞所在的文件?
0x03
记录
书接上回,让我们看看如何解决failed to convert to binary ip data报错
data
经过本人实测,远程调试patch是最可靠的方法
具体操作如下
1.虚拟机选择桥接
vm
2.将在ida的
dbgsrv
目录下linux_server
和linux_server64
文件复制到ubuntu下dbg
linux
3.赋予
linux_server
和linux_server64
可执行权限sudo chmod +x ./linux_server linux_server64
4.
ifconfig
查看虚拟机ipip
5.在ubuntu上运行
linux_server
并运行goahead
sudo qemu-mipsel -g 1234 -L ./ ./bin/goahead
在ida里填好ip,端口
port
6.打断点,运行
jmp
7.修改v0的值为0,即可继续运行
v0
打开
http://192.168.0.105/dir_login.asp
,即可进入登陆界面(不能在ubuntu打开,困扰了我好久😢,用kali打开的,本机也可)kali
搭好之后我们要登录进去,我选择修改前端(也可以patch)
在
_DIR-816.img.extracted/squashfs-root/etc_ro/web
中找到dir_login.asp
,注释掉禁止输入空字符的js即可至于为什么这样做,主要是为了绕过登录代码
这个程序的登录逻辑是从
nvram_bufget
获得用户名和密码放在v2,v3。用户输入的用户名和密码经过base64编码放在var和v7中,解码后的用户名和密码放在v10,v11中v2,v3值为0,当v10,v11为0时即可绕过检查
login
访问
http://192.168.0.5/d_wizard_step1_start.asp
set
找到漏洞点,
ls
测试一下test
不行,那用burp抓下包
error
在datetime处注入命令
cmd
发过去,success
success
详细看下代码,从web获得输入的datetime的值放在var
&unk_47c2b8
为date -s \"%s\"
,var可以由用户控制,存在命令注入code
疑问
复现cve最大的感触是寻找漏洞才是最关键的,这个漏洞出现在cgi上,cgi更容易产生漏洞?
0x04
记录
准备复现一下小米路由器的cve
官网下载
https://www1.miwifi.com/miwifi_download.html
下载稳定版
binwalk -Me miwifi_ra70_firmware_cc424_1.0.168.bin
解压得到
2B4.ubi
,需要ubi_reader
ubi_reader
下载pip install --user ubi_reader
ubireader_extract_images 2B4.ubi
提取出ubifs-root
binwalk -Me img-870537086_vol-ubi_rootfs.ubifs
提取出squashfs-root
小米的前端是lua写的,但是只有编译好的字节码文件,没有源文件,所以我们要进行反编译
对lua进行反编译,我用的是
unluac
反编译器,但不能对小米的程序进行反编译,所幸有的师傅出了专门的反编译工具unluac for MiWifi
我们首先安装反编译器(注意,需要有java环境)
安装完后,对lua文件进行反编译
java -jar /home/tr0upe/tools/unluac_miwifi/build/unluac.jar xqdatacenter.lua > dis.lua
即可得到反编译后的文件由于我没学过lua,分析了好久也没弄清楚,所以先搁置了,去搞环境搭建
这次环境搭建是系统仿真,之前没弄过,遇到了好多问题
arm64的系统仿真需要内核与磁盘镜像,我直接用的ZIKH26师傅的,感谢师傅
按照
ZIKH26
师傅写的运行./net
发现网桥不存在
sudo apt install libvirt-daemon-system libvirt-clients virt-manager
安装 libvirt
相关包error,依赖关系不满足
sudo apt-get update
试一下怀疑是网络问题,ping一下试试
域名解析失败,应该是dns服务器的问题,换个dns试试
sudo vim /etc/resolv.conf
添加如下框住的dns保存后重新ping一下
success,接下来起手
然后
sudo apt install libvirt-daemon-system libvirt-clients virt-manager
安装 libvirt
相关包添加网桥
brctl addbr virbr0
运行
./net.sh
无报错,接着运行./start.sh
发现没有分配ipv4地址,不知道宿主机如何与qemu通信
重启了一下虚拟机,然后打不开了😭
疑问
对一个没学过的语言,逆向出来如何快速进行代码审计,还有就是qemu仿真太折磨人了,叕叕把我的虚拟机干爆了🤮
0x05
记录
上次把虚拟机干爆了,这回重新搭个新的
选择了ubuntu的最新版本24.04
首先是安装环境
安装qemu
安装依赖
安装成功后
ifconfig
查看,virbr0会被分配ip运行
net.sh
后运行start.sh
进入qemu后是无ip的,我们要分配一个
分配完后
ip add
查看然后和宿主机互ping,能ping通就行
ok,接下来我们要将squashfs-root传入qemu
用scp命令传文件进去
在qemu里
unzip
解压/sbin/procd &
,启动进程管理器/etc/init.d/sysapihttpd start
启动sysapihttpd发现缺少
/var/lock/procd_sysapihttpd.lock
这个文件创建相应的文件,重新执行后,usock: No such file or directory报错
创建
var/run/ubus.sock
文件,启动/sbin/ubusd &
检查相关程序启动
nmap在主机扫段口试试
http80端口存在
但是打不开网址,curl报错
用strace跟踪一下
getsockopt
报错,ida patch完后,传回qemu按下图命令覆盖/etc/init.d/sysapihttpd start
重新启动访问ip地址
疑问
qemu仿真问题好多啊,解决完一个又冒出新的😂
缺少仿真思路,遇到问题就卡住了,仿真过程中应该有相通的地方,总结仿真思路,遇见常见的问题也能应对
0x06
记录
复现cve
出现web界面后,我们是登不上去的
原因是只启动了部分服务,按照路由器的流程配置会出现各种各样的问题,我们搭建环境的目的是验证漏洞,所以不需要完整配置,跳过配置界面就行
grep -r "init.html"
查找在哪里重定向至init.html
可以看到跳转的代码
只要满足
XQSysUtil.getInitInfo()
返回true,就可不跳转到init.html
查找相关代码
从
xiaoqiang.XQPreference
中调用get方法获取与 XQConfigs.PREF_IS_INITED
相关的值,并将其存储在局部变量 initted
中只要
initted
为true,则进入到if代码块,返回true只要value值为真,则
return value
value值从
get(config, "common", key)
从上述信息可知,config为
"xiaoqiang"
,key为"INITTED"
执行
value值变为1,可以不重定向至
init.html
重新访问ip,进入到登陆界面
用设定的密码
winmt
登录入路由器后台启动
查看
9090
和9091
端口正常即可运行exp,拿shell
cs基础
根据注释要求完成代码
hw01
lab01
0x07
cisc & risc
- cisc(complex instruction set computer)复杂指令计算机。例如x86,amd64
- risc(reduced instruction set computer)精简指令集计算机,例如mips,arm
mips大小端
- mips大端序
- mipsel小端序
mips寄存器
通用寄存器
寄存器 | 汇编名称 | 解释 |
r0 | $zero | 总是为0 |
r1 | $at | 保留给汇编程序 |
r2-r3 | $v0-$v1 | 储存返回值 |
r4-r7 | $a0-$a3 | 储存参数 |
r8-r15 | $t0-$t7 | 临时变量 |
r16-r23 | $s0-$s7 | 保存 |
r24-r25 | $t8-$t9 | 其他临时变量 |
r26-r27 | $k0-$k1 | 为操作系统保留 |
r28 | $gp | 全局指针 |
r29 | $sp | 栈指针 |
r30 | $fp | 帧指针 |
r31 | $ra | 返回地址 |
mips指令格式
R类型
两个操作数和结果都在寄存器的运算指令。 如sub rd, rs, rt
I类型
- 运算指令: 一个寄存器、 、一个立即数。如 :ori rt, rs, imm16
- LOAD和STORE指令。如:lw rt,rs, imm16
- 条件分支指令。如:beq rs,rt,imm16
J类型
无条件跳转指令。如: j target
mips指令字段含义
op:操作码
rs:第一个源操作数寄存器
rt:第二个源操作数寄存器
rd:结果寄存器
shamt:移位指令的偏移量
func:R类型指令的op字段是
000000
,具体操作由func字段给定。例如:func为100000
,表示加法运算immediate:立即数或load/store指令和分支指令的偏移地址
target address:无条件转移地址的低26位。
常用R型、I型、J型指令编解码表
汇编形式与指令的对应
一条指令为0xaf8020,则对应的汇编形式为?
0xaf8020
的32位:0000 0000 1010 1111 1000 0000 0010 0000指令的前6位为000000,可知这是R类型指令,按照R类型格式
rs = 00101,rt = 01111,rd = 10000, shamt = 00000, funct = 100000
1.根据R类型指令解码表,得知是add操作
2.rs,rt,rd十进制是5,15,16,可知rs,rt,rd为:$a1,$t7,$s0
故汇编为add $s0, $a1, $t7
mips系统调用
Service | Code in $v0对应功能的调用码 | Arguments所需参数 | Results返回值 |
print_int打印一个整型 | $v0 = 1 | $a0 = integer to be printed将要打印的整型赋值给 $a0 | |
print_float打印一个浮点 | $v0 = 2 | $f12 = float to be printed将要打印的浮点赋值给 $f12 | |
print_double打印双精度 | $v0 = 3 | $f12 = double to be printed将要打印的双精度赋值给 $f12 | |
print_string | $v0 = 4 | $a0 = address of string in memory将要打印的字符串的地址赋值给 $a0 | |
read_int | $v0 = 5 | integer returned in $v0将读取的整型赋值给 $v0 | |
read_float读取浮点 | $v0 = 6 | float returned in $v0将读取的浮点赋值给 $v0 | |
read_double读取双精度 | $v0 = 7 | double returned in $v0将读取的双精度赋值给 $v0 | |
read_string读取字符串 | $v0 = 8 | $a0 = memory address of string input buffer将读取的字符串地址赋值给 $a0 $a1 = length of string buffer (n)将读取的字符串长度赋值给 $a1 | |
sbrk动态分配内存 | $v0 = 9 | $a0 = amount需要分配的空间大小 | address in $v0将分配好的空间首地址给 $v0 |
exit退出 | $v0 =10 | ㅤ |
mips汇编分析
该程序输出一个
hello world
1.LI(Load Immediate)指令用于将一个立即数存入一个通用寄存器。
2.LA(Load Address) 指令用于将一个地址或标签存入一个寄存器
data段存放变量
str
的ascii码hello world
text段是要执行的程序,li指令将4载入$v0寄存器中,la指令str地址载入$a0寄存器,syscall进行系统调用,即打印
hello world
0x08
记录
冀信
冀信vpn一掉线就连不上,难绷😅
ROP3
不给libc,又是最喜欢的测libc环节🤣
测出libc本地patch完后打one_gadget
babypie
泄露canary后单字节覆盖
ret2libc
经典ret2libc
rop-gets
静态编译的程序,ropgadget生成rop chain秒了
羊城杯
psstack
总感觉做麻烦了
这道题的核心就栈溢出,不过其他啥也没有,刚刚够覆盖返回地址
保护
主要思路是劫持rbp为bss段,返回地址改到
0x4006c4
重新进行read,因为改了rbp,所以可以写到bss段里第二次read结束,程序运行到
0x4006db
时可以将栈迁移到bss段执行,所以第二次read在bss布置puts泄露libc,最后接上start重新运行程序本来是想打one_gadget的,发现都不行,就同上进行栈迁移,执行
system("/bin/sh")
,结果还是不行,卡在了汇编的一步然后只能打
execve(/bin/sh, 0, 0)
,结果rop链刚好差8字节,只能在进行栈迁移,在进行readhttpd
这道题很有意思,跟固件漏洞很想,主要考察的是代码审计
ida反汇编没有main函数,打开start查看
可以确定
sub_13ed
是main函数,有点疑惑ida没有识别出来程序主要逻辑
照例查看文件
运行程序试试
Internal Error
迅速定位至查询chdir的功能,改变当前的工作目录,类似linux中的
cd
我的电脑没有该目录,创建该目录,重新运行
Bad Request
错误,定位到根据空格读取输入,输入需要类似
a a a
,执行接下来输入需要
Host:
前缀Host:
前缀后需要输入ip格式第三次输入前缀需要是
Content-Length:
Content-Length:
后面要控制长度第一次输入的第一个空格前必须是
get
,第一个空格后以/
开头,第二个空格后是HTTP/1.0
第一次scanf第二个参数,v34 =
/
,v35可以由用户控制然后v35赋给haystack后,
sub_1F74
会检查scanf第二个参数检查会ban掉sh,bin,&,|,;,$,{,},`
接下来会将标准输出,标准错误放在
/dev/null
接下来执行popen,
haystack
是用户能控制的,存在命令注入简单了解下popen, 可以执行系统命令,例如
fp = popen("ls -l", "r");
这样我们只需用
s""h
绕过检查即可弹shell可以看到执行了命令但是没有回显
反弹shell
疑问
这周主要打比赛和培训,算是复习了下基础,主要的疑问是有的程序ida反编译不显示main函数(倒不是很影响做题)
0x09
下载锐捷固件网址
https://reyee.ruijie.com/en-global/support/documents/slide_RGEW1200WirelessRoutersfirmware/#
点击download
binwalk命令解压
binwalk -Me EW_3.0\\(1\\)B11P204_EW1200I_10153001_install_encypto.bin
发现解压失败
说明固件被加密了
对于被加密的固件,一般下列有几种方法
1.找之前未加密的最后一版固件,未加密固件为了升级为加密固件,其中会包含对加密固件的解密,可以找到解密的部分进行研究
2.直接分析加密后的固件,寻找具备特定规律的字节码,编写解密脚本
不过我没在官网上找到未加密的固件,故采用第二种方法分析
010editor打开固件
看见文件末全是
\\x80
一般文件末会填充
\\xff
或\\x00
,这里有大量\\x80
,猜测为单字节异或易得
\\x7f
python decrypt.py EW_3.0\\(1\\)B11P204_EW1200I_10153001_install_encypto.bin decrypt.bin
运行下面的脚本解密解密的文件再用
binwalk -Me decrypt.bin
解密成功拿到解密的文件
注意到
/usr/lib/lua
目录下存放许多lua文件,主要是将前端的数据传给后端在
/usr/lib/lua/luci/controller/eweb
目录下,存放着api.lua
配置了路由
这段代码的含义是访问
/api/auth
路径时,会调用rpc_auth
函数,nil
是一个可选参数,.sysauth = false
表示禁用系统认证。也就是说,访问/api/auth
路径时,不需要用户登录或提供tokenrpc_auth
函数引入了四个模块分别是jsonrpc
http
ltn12
_tbl
_tbl
引入了luci.modules.noauth
,从名称可以看出,这是处理不需要认证的部分http.getenv("HTTP_CONTENT_LENGTH")
获取http请求长度信息,tonumber
将字符串转化为数字,如果大于1000字节,则返回too long data
http.prepare_content("application/json")
设置http响应的Content-Type
为application/json
jsonrpc.handle(_tbl, http.source())
使用jsonrpc
模块的handle
函数,_tbl
传入的是luci.modules.noauth
返回的表,该表包含了login
singleLogin
merge
checkNet
四个函数。http.source
是http的输入流ltn12.pump.all()
将输出传到http.write()
,将结果写入http响应luci.ltn12.pump.all()
将 rawsource
中的数据传输到 decoder:sink()
,即将原始数据解析为 JSON 格式,并存入解码器。stat表示数据传输的状态,若成功则为Truelocal json = decoder:get()
获得解码后的json数据,json的method字段类型为string
则将tbl, json.method
传入resolve函数luci.util.split(method, ".")
将method字段以.
分割,例如传入noauth.merge
分割成{"noauth", "merge"}
if not type(mod) == "table" then break end
检测mod
是否为一个表table
,不是表则退出循环当调用
resolve(mod, "luci.modules.noauth.merge")
时:1.
method
分割成luci
, modules
, noauth
和merge
2.
#path - 1
为33.第一轮循环mod更新为
luci
表,第二轮循环更新为modules
,第三轮更新为noauth
4.最后
type(mod) == "table"
检测是否为表类型,是的话返回True,and只有前面是True时执行,#path
为4,path[#path]
即为merge,or当type(mod) == "table" and rawget(mod, path[#path])
返回false或nil时,执行or后面的表达式,mod = rawget(mod, "merge")
,返回merge函数method字段为
merge
,local res = {luci.util.copcall(method, ...)}
调用luci.util
中的copcall
函数f
为merge,传入coxpcall
函数中oldpcall(coroutine.create, f)
,在一个新的协程(可以理解为一种特殊的线程)中运行函数f
,然后就触发了merge
函数引入了
luci.modules.cmd
模块,调用cmd的devSta.set
函数