TMUCTF2021 Writeup

Puzzle

Life is a puzzle!


给了一张图片,首先使用binwalk提取一下文件,得到一个有密码的压缩文件和一张图片.

观察图片发现,第一位数字表示行号,剩下的数字是在描述上一行数字的个数.

例如,从12->11112是:先写一个1表示行号,上一行有1个1、1个2,加上11和12即可得到11112.

依次类推,得到压缩包的解压密码?61542142311.

输入密码,解压得到flag.txt:

R1pIUEdTe1EzeV9NM19RNDU3NHpfRTRzNzBfVzRhX1U0el9PMV9RM3kwX1c0YV9QdTAwYV9YMGE0en0=

base64解码一下:

GZHPGS{Q3y_M3_Q4574z_E4s70_W4a_U4z_O1_Q3y0_W4a_Pu00a_X0a4z}

ROT13解密一下:

TMUCTF{D3l_Z3_D4574m_R4f70_J4n_H4m_B1_D3l0_J4n_Ch00n_K0n4m}

flag:TMUCTF{D3l_Z3_D4574m_R4f70_J4n_H4m_B1_D3l0_J4n_Ch00n_K0n4m}

Too Much

I found a program that generates the flag of this challenge, but the implementation of one of its functions is not available. This function seems simple, but I am confused because of the large number of numbers! Can you help me find the flag before the competition is over?!


题目给了一个包含1000000个数字的文件numbers.txt,要求编写一个函数:对于给定的x,在numbers.txt中找到两个数y和z,要求满足y != z 和 (y + z) == x,返回不重复的(y,z)的个数.

利用哈希数组的思想,将时间复杂度降到O(n):

def func2(x):
    """
    Returns the number of distinct pairs (y, z) from the numbers in the file "numbers.txt" whose y != z and (y + z) == x
    Note that two pairs (y, z) and (z, y) are considered the same and are counted only once
    """
    with open('numbers.txt', 'r') as f:
        data = [int(_) for _ in f.readlines()]

    length = max(data) + 1
    hash_arr = [False] * (length)

    for t in data:
        hash_arr[t] = True
    ans = 0
    for y in data:
        z = x-y
        if z > 0 and hash_arr[z]:
            if y != z:
                ans += 1
    print(chr(ans//2))
    return ans // 2

def get_flag(res):
    flag = []
    for i in range(len(res)):
        flag.append(chr(func2(res[i])))
    flag = ''.join(flag)
    return flag

if __name__ == "__main__":
    res = [751741232, 519127658, 583555720, 3491231752, 3333111256, 481365731, 982100628, 1001121327, 3520999746,
           915725624, 3218509573, 3621224627, 3270950626, 3321456817, 3091205444, 999888800, 475855017, 448213157,
           3222412857, 820711846, 3710211491, 3119823672, 3333211607, 812955676, 971211391, 3210953872, 289789909,
           781213400, 578265122, 910021887, 653886578, 3712776506, 229812345, 582319118, 1111276998, 1151016390,
           700123328, 1074521304, 3210438183, 817210125, 501231350, 753244584, 3240911853, 415234677, 469125436,
           592610671, 612980665, 291821367, 344199617, 1011100412, 681623864, 897219249, 3132267885, 565913000,
           301203203, 3100544737, 432812663, 1012813485, 510928797, 671553831, 3216409218, 3191288433, 698777123,
           3512778698, 810476845, 3102989588, 3621432709, 812321695, 526486561, 378912454, 3316207359, 623111580,
           344209171, 537454826, 691277475, 2634678623, 1112182335, 792111856, 762989676, 666210267, 871278369,
           581009345, 391231132, 921732469, 717217468, 3101412929, 3101217354, 831912337, 532666530, 701012510,
           601365919, 492699680, 2843119525]
    print("The flag is", get_flag(res))
The flag is TMUCTF{r4nd0m_fl46_f0r_fun!_SzC!$JvnbrRh6kc*1@L!4vMueH1k0xKPJem@vh6Y2&Sb2CJzwjnTfU6wVZyePOK3}

flag:TMUCTF{r4nd0m_fl46_f0r_fun!_SzC!$JvnbrRh6kc*1@L!4vMueH1k0xKPJem@vh6Y2&Sb2CJzwjnTfU6wVZyePOK3}

The Foreign Student

Tarbiat Modares University has a foreign student. His name is Zedmondo. He has a very shady character. He always walks alone, eats alone, and never talks much.There are some rumors about him. Some people say he is a genius sociopath; some say he is just too self-involved. But one thing is obvious; he has a secret.Once, one of the students heard that he was talking about receiving some important documents via a private email. Maybe if we find his email, we can learn about his secret.*


题目是要我们找一个叫Zedmondo的大学生的private email

首先,用Google搜索Zedmondo Tarbiat Modares University,找到这个人的Linkedin主页

Zedmondo 领英

除了一个Github链接外(https://github.com/ZedZini),没有什么其他有价值的信息

浏览Github上的仓库,结合题目中的he has a secret.,一个仓库https://github.com/ZedZini/secretkeyREADME引起了我的注意.

# secretkey
It is a public key. Not really a secret, right?!

README一起的还有一个PGP公钥文件:

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: Keybase OpenPGP v1.0.0
Comment: https://keybase.io/crypto

xsFNBGAqSQ8BEADZtFG6grS2QP9afsA7SmT85TpxcSmG5LGLmSHKgI47ZwS+dPrO
SzChR0Jt3vI7BjA3WVlxQp94XTqRqFrjtJkS2I3nO3I94jhLu0AwfoiskKzyl+tQ
...
-----END PGP PUBLIC KEY BLOCK-----

访问https://keybase.io/crypto,发现网站上已经给出了使用说明:

Using the Keybase command line app
keybase pgp encrypt -m "message" [them]
With GPG or another PGP program
You may import from Keybase to GPG easily and then perform whatever cryptographic actions you want.

# using curl
curl https://keybase.io/[them]/key.asc | gpg --import

# using `keybase pgp pull` which
# imports to GPG key chain for you
keybase follow [them]
keybase pgp pull [them]

参照使用说明:

curl https://raw.githubusercontent.com/ZedZini/secretkey/main/0xEB0B6528-pub.asc | gpg --import

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0  
gpg: 目录‘/Users/zhaoqi219/.gnupg’已创建
gpg: 钥匙箱‘/Users/zhaoqi219/.gnupg/pubring.kbx’已创建
100  6139  100  6139    0     0   4494      0  0:00:01  0:00:01 --:--:--  4490
gpg: /Users/zhaoqi219/.gnupg/trustdb.gpg:建立了信任度数据库
gpg: 密钥 586DD615EB0B6528:公钥 “Zedmondo Zaberini (Nothing to say...) <[email protected]>” 已导入
gpg: 处理的总数:1
gpg:               已导入:1

flag: TMUCTF{[email protected]}

Login

Just login and get the flag!

http://185.235.41.189


访问/robots.txt

if (isset($_GET["password"])) {
    if (hash("md5", $_GET["password"]) == $_GET["password"]) {
        echo "<h1>Here is the flag:</h1>" . $flag;
    } else {
        echo "Try harder!";
    }
}

==是弱类型的比较,字符串在与数字比较前会自动转换为数字;0e\d+这种字符串会被解析为科学计数法的数字,0的多少次方都是零,所以oe\d+ == oe\d+.

所以我们只要找到一个形如oe\d+且哈希后仍为oe\d+的字符串即可.

代码:

import hashlib

i = 0
while True:
    m = hashlib.md5()
    plain = f'0e{i}'
    m.update(plain.encode('utf-8'))
    t = m.hexdigest()
    if t[:2] == '0e' and t[2:].isdigit():
        print(plain, t)
        break
    i += 1
    if not i % 10000:
        print(i)

md5("0e215962017") == "0e291242476940776845150308577824"

flag:TMUCTF{D0_y0u_kn0w_7h3_d1ff3r3nc3_b37w33n_L0053_c0mp4r150n_4nd_57r1c7_c0mp4r150n_1n_PHP!?}

The Devil Never Sleeps

If you put the devil to sleep, you will get the flag successfully. Unfortunately, the devil never sleeps. But what if you use some sleeping pills?

http://194.5.207.57:8080


访问题目链接

To get sleeping pills, navigate to /sleepingpill. To get the flag, navigate to /flag.

访问/sleepingpill得到JWTPublic Key,保存至pub.key

-----BEGIN PUBLIC KEY-----
MIGsMA0GCSqGSIb3DQEBAQUAA4GaADCBlgKBjgD/////////////////////////
/////////////////////////////////////////////////////////////3//
///////////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECAwEAAQ==
-----END PUBLIC KEY-----

访问/flag

{
   "msg": "Missing Pill Header"
}

加上Pill试试,可以看到是一个标准的JWT格式

{
    "msg": "Missing 'Bearer' type in 'Pill' header. Expected 'Pill: Bearer <JWT>'"
}

jwt.io上解析一下JWT

jwt

使用RsaCtfTool生成私钥,保存至private.pem

python RsaCtfTool.py --publickey ./key.pub --private
-----BEGIN RSA PRIVATE KEY-----
MIICmwIBAAKBjgD/////////////////////////////////////////////////
/////////////////////////////////////3/////////////+AAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAECAwEAAQKBjSp/1YAqf9WAKn/VgCp/1YAqf9WAKn/VgCp/1YAqf9WA
Kn/VgCp/1YAqf9WAKn/VgCp/1YAqf9WAKn/VgCp/1YAqVVWqqlVVqqpVVaoAVf+q
AFX/qgBV/6oAVf+qAFX/qgBV/6oAVf+qAFX/qgBV/6oAVf+qAFX/qgBV/6oAVf+q
AFX/qgBV/6oAVf+qAQJMf///////////////////////////////////////////
/////////////////////////////////////////////////////////wJCAf//
////////////////////////////////////////////////////////////////
////////////////////AkxVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWq
qlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqpAkIB
gIB/f4CAf3+AgH9/gIB/f4CAf3+AgH9/gIB/f4CAf3+AgH9/gIB/f4CAf3+AgH9/
gIB/f4CAf3+AgH9/gIB/f38CTHve973ve973ve9773ve973ve973ve+973ve973v
e973vve973ve973ve9773ve973ve973ve+973ve973ve973vve973ve973ve970=
-----END RSA PRIVATE KEY-----

修改payload中的sleeptrueexp9999999999,生成JWT Token并请求/flag

代码:

import jwt

with open('private.pem','r') as f:
    secret = f.read()
print(secret)

dic = {
    "fresh": False,
    "iat": 1631241476,
    "jti": "4b30d7a8-256f-405e-9640-4278728a8602",
    "type": "access",
    "sub": "devil",
    "nbf": 1631241476,
    "exp": 9999999999,
    "sleep": "true",
    "danger": "true"
}
headers = {
    "typ": "JWT",
    "alg": "RS256"
}
token = jwt.encode(dic, secret, headers=headers, algorithm='RS256')
print(token)

flag:TMUCTF{0h_51nn3rm4n_Wh3r3_Y0u_60nn4_Run_70?}

Warmup

A fun visual cryptogr aphy tool will amaze your kids!


题目中给出了两张灰度图ciphered_message.pngsecret.png,先用PIL看一下图片中每个像素点的值,发现均为0或255.

from PIL import Image
im = Image.open('ciphered_message.png')
print(list(im.getdata()))

使用PIL合并两张图片即可.

from PIL import Image

im = Image.open('ciphered_message.png')
im2 = Image.open('secret.png')
assert im.size == im2.size
size = im2.size
print(size)
print(im.mode)
data = [x for x in im.getdata()]
for index,x in enumerate(im2.getdata()):
    if x and data[index]:
        data[index] = 255
    else:
        data[index] = 0

im = Image.new('1',size)
im.putdata(data)
im.save('ans.png')

TMUCTF

flag:TMUCTF{W3_h0p3_y0u_3nj0y_7h15_c0mp371710n_4nd_7h4nk_y0u!}

您的支持是我继续创作最大的动力!

欢迎关注我的其它发布渠道