1:特殊的标记

先通过一个题目来认知:

题目截图

这题根据php源码,可以很快了解题目意图。总之就是在一大堆限制中构造payload读取flag。

这个题目,首先要满足$_GET['flag']$exam字符串长度相等,$exam的长度可以确定。并且,不能通过参数数组绕过。

下面一大堆正则表达式,上面说过,由于存在字符长度的检测(且不能通过数组绕过)所以这里的正则就不能通过数组的方式绕过了。

再下面就是一个eval函数,payload构造的重点就在于此。由于正则表达式的存在,eval中就不能使用PHP函数了。echo等关键方法也被过滤了。

所以这里就引出PHP标记<?=?>的特性了。

PHP 有一个 echo 标记简写 <?=, 它是更完整的 <?php echo 的简写形式。

PHP标记官方文档

于是用这个标记,就能构造出打印$flag的payload了。由于flag字符被过滤了,可以使用字符串操作构造出flag

此题的payload:

1
/?flag=$a='xlag';$a{0}='f';?>1111111111111111111<?=$$a?>

中间的1111是占位符,满足前面字符长度的要求。

2. SQL单引号转义

从一个题目里分析:

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
<?php
#GOAL: get password from admin;
#password is the flag
error_reporting(0);
require 'db.inc.php';
function clean($str){
if(get_magic_quotes_gpc()){
$str=stripslashes($str);
}
return htmlentities($str, ENT_QUOTES);
}

$username = @clean((string)$_GET['username']);
$password = @clean((string)$_GET['password']);

$query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';
$result=mysql_query($query);
if(!$result mysql_num_rows($result) < 1){
die('Invalid password!');
}

$row = mysql_fetch_assoc($result);

echo "Hello ".$row['name']."</br>";
echo "Your password is:".$row['pass']."</br>";

这是一个SQL注入题。题目提供了源码。

由于上面的clean()函数会让构造的payload中的引号转义了,就没法通过添加引号进行SQL注入了。

不过,我们可以用手段将SQL query 中的 ' 转义了。比如在payload末端加入 \(反斜杠)将query中的name的后面一个 ' 转义。例如,得到下面的结果:

1
SELECT * FROM users WHERE name='admin\' AND pass='2333'

这样得到name='admin\' AND pass=' pass被包含到name里面去了。于是输入的password就暴露了出来,可以直接注入SQL语句了(注意:pass后面还有一个 ' 但是只需要把它注释掉即可)。

于是,根据题目意思,构造payload。可以看到,payload中没有一个分号

1
/?username=\&password=or%201=1%23

注:%23为#的URL编码。

文件包含、读取

Session条件竞争

利用条件:

  • PHP应用可以任意读取文件
  • PHP>5.4.0
  • session.upload_progress.enabled 选项开启(默认)
  • 一般读取文件加了很多限制条件,至少限制了flag文件的直接读取。(如果能直接读取flag为何还要大费周章)

通过POST上传文件的同时添加 PHP_SESSION_UPLOAD_PROGRESS form-data参数和自定义的**PHPSESSID** cookieform-data可以是任意值(PHP恶意代码),发送后form-data值会临时保存在session文件(文件路径以及命名方法同PHP session配置)中,如当cookie值为abcd时对应路径为**/tmp/sess_abcd**(默认)。

不过,如前面提到的临时,POST文件上传结束(请求结束后),session文件中的内容就会自动清空(默认)。所以,通过”竞争“,进行多线程请求,使session文件中保持内容的概率增加,然后再通过已有的include或文件读取方法将其中的PHP代码执行达到目的。

注:此方法并不需要有真实的文件上传接口,也不用关心上传文件的内容(尽量大一些,可以延长session存在时间)