互联网本来是安全的,自从有了研究安全的人之后,互联网就变得不安全了。

——《白帽子讲Web安全》

官方WP

Reverse

零基础真的零基础,就这么稀里糊涂地做了几道题。

baby_xor

下载文件拖入IDA看一眼主函数,鉴定为纯纯的异或。(数组具体数据不放了)

程序的本质是我们输入的字符串s中的每个字符与其下标进行按位异或运算,再与0x46进行按位异或运算,与data中对应的元素比较。
异或可逆,data[i] = i ^ s[i] ^ 0x46 等价于 s[i] = data[i] ^ i ^ 0x46
破解程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
data = [0 for i in range(41)]
ans = [0 for i in range(41)]
data[0] = 18
data[1] = 20
data[2] = 7
# ...
data[39] = 83
data[40] = 19
for i in range(41):
ans[i]=chr(i ^ data[i] ^ 0x46)

for i in range(41):
print(ans[i],end="")

运行得Flag:

byte_code

查看文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 1           0 LOAD_CONST               0 (114)
2 LOAD_CONST 1 (101)
4 LOAD_CONST 2 (118)
6 LOAD_CONST 1 (101)
8 LOAD_CONST 0 (114)
# ... ...

19 466 LOAD_NAME 9 (print)
468 LOAD_NAME 10 (chr)
470 LOAD_NAME 6 (c)
472 LOAD_NAME 8 (i)
474 BINARY_SUBSCR
476 CALL_FUNCTION 1
478 LOAD_CONST 94 ('')
480 LOAD_CONST 95 (('end',))
482 CALL_FUNCTION_KW 2
484 POP_TOP
486 EXTENDED_ARG 1
488 JUMP_ABSOLUTE 418
>> 490 LOAD_CONST 99 (None)
492 RETURN_VALUE

pyc字节码。

pyc是一种二进制文件,是由py文件经过编译后,生成的文件,是一种byte code,py文件变成pyc文件后,加载的速度有所提高,而且pyc是一种跨平台的字节码,是由python的虚拟机来执行的。

去网上搜了一个小时的pyc反编译工具,无果,最后通过一些博客的帮助~手撸撸出来了~。
具体过程不细嗦,谁做谁知道,给出反编译后的py程序如下(不足之处欢迎指出):

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
import dis

def func():
a = [114,101,118,101,114,115,101,95,116,104,101,95,98,121,116,101]
b = [99,111,100,101,95,116,111,95,103,101,116,95,102,108,97,103]
e = [80,115,193,24,226,237,202,212,126,46,205,208,215,135,228,199,63,159,117,52,254,247,0,133,163,248,47,115,109,248,236,68]
pos = [9,6,15,10,1,0,11,7,4,12,5,3,8,2,14,13]
d = [335833164,1155265242,627920619,1951749419,1931742276,856821608,489891514,366025591,1256805508,1106091325,128288025,234430359,314915121,249627427,207058976,1573143998,1443233295,245654538,1628003955,220633541,1412601456,1029130440,1556565611,1644777223,853364248,58316711,734735924,1745226113,1441619500,1426836945,500084794,1534413607]
c = [0 for i in range(32)]
for i in range(31):
print(chr(c[i]),end="")
print(chr(c[31]))
for i in range(16):
a[i]=(a[i]+d[i]) ^ b[pos[i]]
for i in range(16):
b[i] = b[i] ^ a[pos[i]]
c = a+b
for i in range(32):
c[i] = c[i]*d[i]%256
c[i] ^= e[i]
print(chr(c[i]),end="")

if __name__ == "__main__":
func()

运行得Flag:

ez_maze

拖入IDA,shift+F12查字符串,观察到Flag编码信息和不明长字符串:

结合maze题的信息,复制长字符串至记事本,~数字符个数~拖入家中常备的统计字符个数的脚本,得字符个数为5551=91x61,分隔字符后迷宫如下(ps.必须使用等宽字体,这里使用了幼圆):

没看到起点和终点啊。于是回去读IDA,无果,利用hint“如果不能反混淆就大胆猜测这个题在干什么吧”,我大胆猜了一把:从左上走到右下
结合之前IDA里看到的"WASD"可知迷宫移动使用WASD键。
结合之前IDA里看到的"Wrong Path, Maybe it’s not the best solution."可知需要寻找最短路径。
最短路径算法不会,同寝的ACM👴不肯帮我,遂大胆猜了另一把。得路径如下:
DSDWDDSDWDDDSASSSSDDSSDSSDWDWAWWWWASAAWDWDWDDSDSDDWWDDSASSSDDDWAWWWDWDDSSDSSDSDSDSSAAAAAWWASASASDSDWDDSDDDWDDSASDSSDWDWWDSSSSSSSAWAAWAAAAASAAASASDDDDWDWDSSSDDWWDSSSDSASSAAWAWWAASSDSDSSDSDDSDDSASDD
MD532位大写加密一下包上大括号抱着试一试的心态上交,居然对了。
猜想之前进行了抽象的操作,现在看来真是痛定思痛痛何如哉,这样的抽象文化对🐭🐭这个新手来说还是为时尚早了。

Crypto

半个月前刚在网安导论课上学了RSA就来实践力!

T0ni’s_RSA

下载文件IDLE打开

1
2
3
4
5
6
assert len(flag)==48
flag1=flag[0:12]
flag2=flag[12:24]
flag3=flag[24:36]
flag4=flag[36:48]

可知长度为48的Flag被等分成了四部分,每一部分使用了不同的RSA算法加密。
具体如下(省略具体数据):

Flag1

1
2
3
4
5
6
7
8
9
10
11
12
print("=====================================flag1")
m=bytes_to_long(flag1)
p=getPrime(1024)
q=getPrime(1024)
e=65537
n=p*q
c=powmod(m,e,n)
print("p =",p)
print("q =",q)
print("e =",e)
print("c =",c)

常规的RSA加密,知道p、q可以求phi = (p-1)(q-1),得到私钥d = gmpy2.invert(e, phi),进而求得m = pow(c,d,n),即Flag1。
代码如下:

1
2
3
4
5
6
7
import gmpy2
from Crypto.Util.number import *

phi = (p-1)*(q-1)
d = gmpy2.invert(e, phi)
print(long_to_bytes(pow(c,d,n)))
# Flag1 = b'TSCTF-J{T0ni'

Flag2

1
2
3
4
5
6
7
8
9
10
11
print("=====================================flag2")
m=bytes_to_long(flag2)
p=getPrime(64)
q=getPrime(64)
e=65537
n=p*q
c=powmod(m,e,n)
print("n =",n)
print("e =",e)
print("c =",c)

n不太大,可以尝试因式分解,分解得p = 10044079891992334031 q = 11695298459661145481
余下做法同Flag1,得Flag2 = b'ii_is_the_mo'

Flag3

1
2
3
4
5
6
7
8
9
10
11
print("=====================================flag3")
m=bytes_to_long(flag3)
p=getPrime(1024)
q=next_prime(p)
e=65537
n=p*q
c=powmod(m,e,n)
print("n =",n)
print("e =",e)
print("c =",c)

不会,面向搜索引擎编程,得到信息如下:

摘录代码如下(部分测试代码略去):

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
import gmpy2
from Crypto.Util.number import *

class my_RSA:
def gen_n1(BITS):
# 生成很接近的p、q,具体代码略

def encrypt(m,n,e):
# 对明文m进行编码,具体代码略

def decrypt(p,q,e,n,c):
phi = (p-1) * (q-1)
d = inverse(e,phi)
m = pow(c,d,n)
pt = long_to_bytes(m)
return pt.decode("utf-8")
# 对密文n进行解码

def fermat_method(nn):
a = gmpy2.isqrt(nn) + 1
k = a**2 - nn
while not gmpy2.is_square(k):
a += 1
k = a**2-nn
return (gmpy2.isqrt(k) + a),(a - gmpy2.isqrt(k))
# 核心程序,获取p、q

p , q = my_RSA.fermat_method(n)
print(my_RSA.decrypt(p, q, e, n1, c))
# Flag3 = st_handsome_

费马分解什么的完全不懂捏,就这样作为脚本小子搞到了Flag3(笑)。
写完WP学一下罢。

Flag4

1
2
3
4
5
6
7
8
9
10
11
print("=====================================flag4")
m=bytes_to_long(flag4)
p=getPrime(1024)
q=getPrime(1024)
e=7
n=p*q
c=powmod(m,e,n)
print("n =",n)
print("e =",e)
print("c =",c)

参考资料得知可以通过低指数爆破得出明文。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import gmpy2
from Crypto.Util.number import *

def de(c, e, n):
k = 0
while True:
m = c + n*k
result, flag = gmpy2.iroot(m, e)
if True == flag:
return result
k += 1

m=de(c,e,n)
print(m)
print(long_to_bytes(m))
# Flag4 = b'boy_in_BUPT}'

Flag

综上,Flag为TSCTF-J{T0niii_is_the_most_handsome_boy_in_BUPT}
如题面所述,不愧是至理名言(V我50

Nonograms

数织
TSC出来基本就是TSCTF-J{}了,需要考虑的是中间五个全角的玩意。
字出来第一想法旗开得胜,然后去做了第五个,一眼鉴定为感叹号。
于是开始输Flag:

TSCTF-J{旗开得胜!}(全角感叹号)不对
TSCTF-J{旗开得胜!}(半角感叹号)不对
TSCTF-J{旗开得胜! }(半角感叹号+半角空格)不对

Dinner是我了。老老实实回去做。最后一个字做到一半发现另有玄只因,重新输Flag TSCTF-J{旗开得勝!} 过了。

锟斤拷烫烫烫

文件内容如下:

1
烫烫烫锟斤拷/烫烫烫锟斤拷锟斤拷锟斤拷/烫烫烫锟斤拷锟斤拷烫烫烫/烫烫烫锟斤拷锟斤拷烫烫烫/烫烫烫锟斤拷烫烫烫/烫烫烫烫烫烫锟斤拷锟斤拷/烫烫烫锟斤拷锟斤拷/锟斤拷锟斤拷烫烫烫锟斤拷/烫烫烫烫烫烫/锟斤拷烫烫烫烫烫烫烫烫烫/烫烫烫锟斤拷锟斤拷烫烫烫/烫烫烫锟斤拷锟斤拷烫烫烫/烫烫烫烫烫烫锟斤拷烫烫烫/锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷/锟斤拷烫烫烫锟斤拷锟斤拷/锟斤拷锟斤拷烫烫烫锟斤拷/烫烫烫锟斤拷/锟斤拷烫烫烫烫烫烫烫烫烫/锟斤拷锟斤拷烫烫烫/锟斤拷烫烫烫烫烫烫/锟斤拷锟斤拷锟斤拷锟斤拷烫烫烫/烫烫烫烫烫烫锟斤拷锟斤拷/锟斤拷锟斤拷锟斤拷烫烫烫烫烫烫/烫烫烫烫烫烫锟斤拷锟斤拷/烫烫烫烫烫烫/锟斤拷锟斤拷锟斤拷烫烫烫/锟斤拷烫烫烫烫烫烫/烫烫烫烫烫烫锟斤拷/烫烫烫锟斤拷烫烫烫/锟斤拷锟斤拷锟斤拷锟斤拷烫烫烫/锟斤拷烫烫烫锟斤拷锟斤拷/锟斤拷锟斤拷锟斤拷烫烫烫/烫烫烫锟斤拷/烫烫烫锟斤拷烫烫烫烫烫烫/烫烫烫锟斤拷锟斤拷锟斤拷烫烫烫/烫烫烫锟斤拷锟斤拷锟斤拷烫烫烫/烫烫烫锟斤拷锟斤拷锟斤拷烫烫烫/烫烫烫锟斤拷锟斤拷锟斤拷烫烫烫/烫烫烫锟斤拷锟斤拷锟斤拷烫烫烫/烫烫烫锟斤拷锟斤拷锟斤拷烫烫烫

第一想法是转码问题,但是单纯的转码不会单纯出现这么整齐的“锟斤拷烫烫烫”,可能掺杂部分破碎的“锟届瀿锟斤拷雮傡锟”等,遂另辟蹊径。
结合hint“永不消逝的电波”,通过观察猜想是摩斯电码,多次尝试后得烫烫烫 = - 锟斤拷 = .
于是编码如下:

1
-. -... -..- -..- -.- --.. -.. ..-. -- .--- -..- -..- --.- ..... .-.. ..-. -. .--- ..- .-- ....- --.. ...-- --.. -- ...- .-- --. -.- ....- .-.. ...- -. -.-- -...- -...- -...- -...- -...- -...-

这个手工编码的过程着实让我体验了一把电报员的感觉。
解码如下:
nbxxkzdfmjxxq5lfnjuw4z3zmvwgk4lvny======
观察后面的六个等号猜测是Base32,字母转大写后放入赛博厨房爆炒后得到houdeboxuejingyeleqvn,包上大括号后提交。

Mathematics

脚本小子提供链接:点我!
摘录代码如下:

1
2
3
4
5
6
7
8
import gmpy2

f1 = pow(5, e1*e2, N) * pow(c1, e2, N)
f2 = pow(2, e1*e2, N) * pow(c2, e1, N)
q = abs(gmpy2.gcd(N, f1-f2))
p = N//q
print(N, p, q, N-p*q, sep='\n')

得p、q,后续步骤略。

Misc&Abstract

北邮人之声

拿了个简单题目的抽象一血。
拖入Au反向一下进行英语幺零贰肆级听力测试,通过单词首字母得Flag = TSCTF-J{WELCOMETOBUPT}

Just_Play

忍不了,一拳地把打球爆!妈的,忍不了,一把拳地球打爆!妈的,忍不了,一拳地把球打爆!妈的,忍不了,一拳把球地打爆!妈的不忍了,一拳地把球打爆!妈的,忍不了,一把拳地球打爆!妈的,忍不了,一拳把球地打爆!妈的,忍不了,一拳地把球打爆!妈的,忍不了,一拳把球地打爆!妈的,忍不了,一-拳把球地打爆!妈的,忍不了,一把拳地球打爆!妈的,忍不了,一把拳地球打爆!妈的,忍不了,一把拳地球打爆!妈的,忍不了,一拳地把球打爆!妈的,忍不了,一拳把球地打爆!妈的,忍不了,一拳把球地打爆!妈的,忍不了,一拳把球地打爆!妈的,忍不了,一把拳地球打爆!杀 杀杀! !好可怕杀杀杀杀杀杀勾上拳!下勾拳!左勾拳!右勾拳!扫堂腿!回旋踢!这是蜘吃蛛耳屎,这是龙卷风毁摧停车场!这羚是羊蹬,这是山羊跳!乌鸦坐~飞机!老走鼠迷宫!大象踢腿!愤怒章的鱼!巨砍斧大树!彻疯底狂!彻底疯狂!彻底疯狂!彻底疯狂!彻底疯狂!彻底疯狂!彻疯底狂!彻底疯狂!彻疯底狂!(怒吼)(变成猴子)(飞进原始森林)(荡树藤)(创飞路过吃香蕉的猴子)(怒吼)(变成猴子)(飞进原始森林)(荡树藤)(创飞路过吃香蕉的猴子)(怒吼)(变成猴子)(飞进原始森林)(荡树藤)

还有个鼠鼠的听力不放了。

EasterEgg

前来朝拜

来自理塘的下一代赛博编程语言

Abstract_culture_revenge

抽象得无法呼吸。

莫(莫言写了《蛙》)愁(丑牛)前(K代表千)路(录制按钮)无(五点)知(知识)己(鸡尾酒)
天(八卦乾代表天)下(下弦月)谁(水瓶座)人(不知道)不(占卜)识(矢)君(菌)

写在最后

未知攻,焉能防。
通过这次TSCTF-J真的学到很多,结识了各路大牛,领略了赛博理塘的抽象文化,也认识到了自己的不足(尤其是web和pwn一道都做不出)
最终成绩Reverse*3+Crypto*4+Misc*3+Abstract*4=2440分,排名13位。