HGame 2020 week1 WriteUp

前言

期待已久的hgame_week1终于开始了!刚开始的Web马上就ak了,后面几天都在做CryptoMisc,差不多是一天一道的节奏,不得不说,密码学和杂项实在是太难了!!!我脑子都炸了,刚刚ak了Misc,现在还差一题才能把Cryptoak。

Web

第一周的web还是蛮有意思的。本来Code Worldcxk的两题卡了蛮久,做出来才发现原来这两题是最简单的…啊还是我太菜了。

Cosmos 的博客

打开题目看到

我们可以看到这里有加粗的版本管理工具GitHub,于是马上想到了.git版本泄露,于是访问/.git。但是却返回404 Not Found,眼看着一血马上从眼前溜走,我赶紧扫一下站

看到这里有一个目录/.git/config,这应该就是git信息储存的目录,访问它我们就应该能获得项目在Github上的地址。

然后访问项目地址,进入了GitHub,想着在commits历史里也许会有flag,点进去看有三个历史记录

一个一个翻看,找到了flag

放到在线base64解密网站,就能拿到flag啦!

还好我手快,最后拿到了第三血!白嫖了0.5分,开心,这是hgame的第一题,是个好兆头!

hgame{g1t_le@k_1s_danger0us_!!!!}

接 头 霸 王

首先访问

既然是come form,又是接头霸王,那么很明显就是要改请求头然后发包了。首先推荐Chrome插件Restlet Client

  • 首先改Refererhttps://vidar.club/
  • 然后要我visit it locally,于是改X-Forwarded-For127.0.0.1
  • 接着要用Cosmos Brower,于是改User_AgentCosmosBrower
  • 完成了之后要我改请求方式为POST,发完之后就是这题最折磨我的地方了!
  • 他说:The flag will be updated after 2077, please wait for it patiently.

在这里我纠结了好久好久,先改Date2077以后的一个时间,没用,然后改电脑本地时间,也不行,尝试了好久,在第二天早上猛然醒悟,再认真地看了看返回头,发现有一个新东西Last_Modified: Fri, 01 Jan 2077 00:00:00 GMT,尝试了发送If-Modified-Since,又失败,抱着试一试的心态,发送了If-Unmodified-Since,居然成功了!!!终于拿到了flag

hgame{W0w!Your_heads_@re_s0_many!}

做完题目后去找了🍆,问完才知道为什么If-Modified-Since不能拿到flag,原来这是一个逻辑问题,我们发包的时间是现在,这时候flag还没有被上传,如果用If-Unmodified-Since应该就可以绕过这个逻辑问题了。

Code World

首先访问

直接就403 Not Forbidden,然后我们打开F12看一下Console

我们看到这里有一个302跳转,于是想到了抓包

这里又是405 Not Allowed,又是nginx,我在这里用了各种单词的组合方式Google,都搜不到我想要的东西,偶然间搜到这一篇,它提到如果把请求方式改一改也许会遇到不一样的情况,于是我改GETPOST,总算不是405了!

于是就是愉快的提交参数啦!先来一个/?a=1+9

提示我再想想?那么我把+urlencode提交再试一试吧。访问/?a=1%2b9

成功拿到flag

hgame{C0d3_1s_s0_S@_sO_C0ol!}

🐔尼泰玫

这个小游戏贼好玩,我先用普通模式玩通关了才开始做题23333。首先可以看出的是,这个游戏是靠Javascript运行的。通关条件是分数达到30000分

所以我们找到网站的源文件Sources,找到里面的的JS文件,看到一个skills.js,这应该就是技能文件了,在里面发现了一个隐藏的技能Q技能

1
class SkillQ extends Skill{constructor(main){super(main,'意念控球','','cxk使用意念控制球转向一次,直接命中最近的一个砖块',10,1000,'Q');}

我悄悄地把技能CD积分消耗都改成0

1
super(main,'意念控球','','cxk使用意念控制球转向一次,直接命中最近的一个砖块',0,0,'Q')

然后愉快的玩起了游戏…然后我遗憾地发现,我通关了还是只有10000+分,直接哭了,看来还是要找其他的捷径。我们接着看其他的JS文件,找到一个game.js,在第一行我发现一个有关分数的定义

1
class Game{storageScore=0;globalScore=0;

于是我悄悄地把globalScore改成30001,然后把球一掉,就能取得flag了!

hgame{j4vASc1pt_w1ll_tel1_y0u_someth1n9_u5efu1?!}

Pwn

提前预习了一下pwn,签到题还是能做一做的。

Hard_AAAAA

首先把程序扔进ida

看到这里有一个gets()函数,于是想到应该会有栈溢出。接着分析,这里还有一个if条件判断函数,把v5上的值通过memcmp()函数与字符串0O0o进行比较,注意是字符串!顺便看一下sv5的地址。s的地址是0xACv5的地址是0x31,他们是连在一起的。那么我们的目的就很明确了,就是通过gets()函数,把0xAc0x31这段内存覆盖,然后赋值给v5

这里有一个坑,很不幸我踩了,幸亏得到了Cosmos学长的提醒。我们重新分析一下memcmp()函数,读取的是7个字符,比较的确是4个字符?于是我们看一下字符串0O0o所在的内存。

所以其实比较的是'0O0o'+'\0'+'O0'7个字符,而因为\0在字符串里又有截断符的意思,所以我们需要另外传入。于是Payload如下

运行脚本连上远程服务器

hgame{0OoO0oo0O0Oo}

One_Shot

通过这道题,我学会了基本的动态调试…好棒!夸夸自己

首先把程序抛进ida

我们首先看第二个scanf()函数,这个v4是一个指针,我们可以输入任何值以更改指针指向的位置,然后下一行*v4=1会令这个位置的值变为1。那么这里又有什么玄机呢?我们继续看看name所在的位置。

可以看到nameflag在内存上是连在一起的,那么问题就好解决了。在scanf()name进行输入时,函数会自动在字符串末尾加一个\x00作为截断符。而我们可以通过指针的任意位置写入,把字符串的末尾改为任意一个字符,就可以在最后输出的时候把flag一起输出。

脚本如下

Crypto

InfantRSA

首先Google一下了解了RSA是什么东西,然后直接去Github上找到了脚本合集,这里放出Github链接,于是找到对应的脚本,把p,q,C对应的填进去,跑一跑就出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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!usr/bin/env python
# coding = utf-8


def fastExpMod(b, e, m):
result = 1
while e != 0:
if (e & 1) == 1:
result = (result * b) % m
e >>= 1
b = (b*b) % m
return result

def computeD(fn, e):
(x, y, r) = extendedGCD(fn, e)
if y < 0:
return fn + y
return y


def extendedGCD(a, b):
if b == 0:
return (1, 0, a)
x1 = 1
y1 = 0
x2 = 0
y2 = 1
while b != 0:
q = a / b
r = a % b
a = b
b = r
x = x1 - q*x2
x1 = x2
x2 = x
y = y1 - q*y2
y1 = y2
y2 = y
return(x1, y1, a)


def decryption(C, d, n):
return fastExpMod(C, d, n)


p = 681782737450022065655472455411
q = 675274897132088253519831953441
n = p * q
fn = (p - 1) * (q - 1)
e = 13
d = computeD(fn, e)
C = 275698465082361070145173688411496311542172902608559859019841
M = decryption(C, d, n)
flag = str(hex(M))[2:-1]
print(d)
print(flag.decode('hex'))

flag就出来了

hgame{t3Xt6O0k_R5A!!!}

Affine

这题的解题关键在两点:

  • 我们已经知道了hgame{}A8I5z{}的对应关系
  • MODiii都是已知的或可求的,以此我们可以列出方程组解出AB

两个未知量两个方程就可以求解,于是我们找到前两组字母的对应关系

1
2
# 第一组i=12,ii=46,MOD=62
# 第二组i=6,ii=33,MOD=62

于是列出方程组

1
2
(12*A+B)%62=46
(6*A+B)%62=33

加加减减可以得出

1
2
A%62=13
B%62=14

AB代回

1
ii=(A*i+B)%MOD

化简得

1
(13*i%62)=ii-14

于是就得到了形如ax=b(mod n)的模线性方程,从网上拉了一个脚本下来,自己改改,最后得到

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
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 求解最大公约数
def ext_euclid(a, b):
old_s, s = 1, 0
old_t, t = 0, 1
old_r, r = a, b
if b == 0:
return (1, a)
else:
while(r != 0):
q = old_r//r
old_r, r = r, old_r-q*r
old_s, s = s, old_s-q*s
old_t, t = t, old_t-q*t
return (old_s, old_r)


flag = ''
TABLE = 'zxcvbnmasdfghjklqwertyuiop1234567890QWERTYUIOPASDFGHJKLZXCVBNM'
MOD = len(TABLE)
secret = "A8I5z{xr1A_J7ha_vG_TpH410}"

for b in secret:
ii = TABLE.find(b)
if ii == -1:
flag += b
else:
a = 13
n = 62
b = ii - 14
(i, d) = ext_euclid(a, n)
i = (i * (b / d)) % n #求模线性方程的一个特解
for j in range(d):
while i < 0:
i = i + j * (b / d)
if i > 0:
break
flag += TABLE[i]
print(flag)

跑一跑就得到flag

hgame{M4th_u5Ed_iN_cRYpt0}

Reorder

连上远端服务器,随意输入字符串,都会返回一段被重新排序的字符串,在出题人的提示下,我不断尝试,终于找到了这个排序的一点小规律,即每一次连接所用的排序方法都是不同的。但每一种排序方法都有一个共同点:排序时以8个字符为单位,对每一个单位分别排序后输出结果。

最关键的是!!!在每次连接结束时,都会打印一行乱七八糟的字符,但仔细观察可以发现,这一串字符串确实是flag被打乱后的样子!!!那么解题思路就明确了,首先我们要确定某一次的排序方法,然后获得这一次被打乱后的flag,跑一跑脚本就能得出flag了。

为了方便找到排序方法,我们输入一串有规律的字符串,看看输出。第二次输入是为了验证每8个字符为一个单位,可以看出这16个字符中,前8个和后8个的顺序是一模一样的。

于是用肉眼观察法,找到字母的对应顺序,写出排序脚本。

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
32
33
34
#!usr/bin/env python

b = list('+IUm5mptgheLb{$j')
a = list('xxxxxxxxxxxxxxxx')
d = list('inTe0!!T_3R}PmAu')
c = list('xxxxxxxxxxxxxxxx')

def reorder(a, b):
a[10] = b[0] # k
a[12] = b[1] # m
a[7] = b[2] # h
a[3] = b[3] # d
a[11] = b[4] # l
a[13] = b[5] # n
a[14] = b[6] # o
a[9] = b[7] # j
a[1] = b[8] # b
a[0] = b[9] # a
a[4] = b[10] # e
a[15] = b[11] # p
a[2] = b[12] # c
a[5] = b[13] # f
a[8] = b[14] # i
a[6] = b[15] # g



def output(a):
for i in range(len(a)):
print(a[i], end='')
reorder(a,b)
output(a)
reorder(c,d)
output(c)

拿到flag

这里有个小小的坑!注意字符串开头应该是hgame,而这里是hgbme,还好我留了个心眼,不然还以为拿到了错的flag

hgame{jU$t+5ImpL3_PeRmuTATi0n!!}

Misc

欢迎参加HGame!

我们把那一串字符串放到百度里搜,搜到了一个贴吧问题,里面的大致内容是用base64解码,那么问题就明确了,我们把这一串字符串放到在线base64解码工具里,得到了一串摩斯电码

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

再找一个在线摩斯密码解码

得到w3lc0me to 2020 hgam3,然后处理一下,得到flag

hgame{W3LC0ME_TO_2020_HGAM3}

壁纸

把压缩包下载以后解压,里面有一张图片,用记事本打开,看见文件末尾有一句话

Password is Picture ID,看到密码,想到了压缩包,于是看一看这张图片是否是图种。用binwalk分析一波,果然藏着一个压缩包,然后用foremost分离图片和压缩包。

压缩包被加密了,密码就是图片的ID,,合理猜想应该是Pixiv ID,于是把这张图放进Google识图里,搜到图片之后找到ID,然后把ID作为密码解压压缩包。

pixiv上去搜这张图片,搜到ID76953815Google真好用呀!

解压后拿到一个flag.txt,打开后是一串Unicode编码

因为Unicode编码的格式为\uxxxx,所以我们用00补齐,得到

1
\u0068\u0067\u0061\u006d\u0065\u007b\u0044\u006f\u005f\u0079\u0030\u0075\u005f\u004b\u006e\u004f\u0057\u005f\u0075\u004e\u0069\u0043\u0030\u0064\u0033\u003f\u007d

然后放到在线工具上解码

hgame{Do_y0u_KnOW_uNiC0d3?}

克苏鲁神话

这道题是我最后做出来的Misc,之前有各种愚蠢的想法,好在有了ObjectNotFound学长的帮助,总算是把这道题做出来了。

解压出来后有一个.txt.zip,压缩包有密码,且密码无法解出,但观察可以发现,压缩包里的一个Bacon.txt已经给我们了,于是可以想到用明文攻击破解压缩包密码,所以我把Bacon.txt改为Bacon.zip,然后上工具AAPR

它会将破解后的压缩包保存在另外一个文件夹中,这样我们就可以看到机密压缩包里的内容了。然后打开压缩包,里面一个.txt,一个.doc.doc文档还需要密码,这时候我们就可以去看看.txt里有什么东西。

看到这里,其实题目的提示已经很明显了,又是Bacon,又是大小写混搭的,其实这里是一个培根密码,于是上网找了Python解密脚本,把两种加密方式都试一下,跑一跑就出来密码了。

密码:FLAGHIDDENINDOC,去打开.doc文件,里面没有flag,网上搜一搜.doc文件通常隐写,于是打开隐藏文字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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# coding:utf8

import re

alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

first_cipher = ["aaaaa", "aaaab", "aaaba", "aaabb", "aabaa", "aabab", "aabba", "aabbb", "abaaa", "abaab", "ababa", "ababb","abbaa", "abbab", "abbba", "abbbb", "baaaa", "baaab", "baaba", "baabb", "babaa", "babab", "babba", "babbb", "bbaaa", "bbaab"]

second_cipher = ["AAAAA", "AAAAB", "AAABA", "AAABB", "AABAA", "AABAB", "AABBA",
"AABBB", "ABAAA", "ABAAA", "ABAAB", "ABABA", "ABABB", "ABBAA",
"ABBAB", "ABBBA", "ABBBB", "BAAAA", "BAAAB", "BAABA",
"BAABB", "BAABB", "BABAA", "BABAB", "BABBA", "BABBB",]


def encode():
upper_flag = False # 用于判断输入是否为大写
string = raw_input("please input string to encode:\n")
if string.isupper():
upper_flag = True
string = string.lower()
e_string1 = ""
e_string2 = ""
for index in string:
for i in range(0, 26):
if index == alphabet[i]:
e_string1 += first_cipher[i]
e_string2 += second_cipher[i]
break
if upper_flag:
e_string1 = e_string1.upper()
e_string2 = e_string2.upper()
print "first encode method result is:\n"+e_string1
print "second encode method result is:\n"+e_string2
return


def decode():
upper_flag = False # 用于判断输入是否为大写
e_string = raw_input("please input string to decode:\n")
if e_string.isupper():
upper_flag = True
e_string = e_string.lower()
e_array = re.findall(".{5}", e_string)
d_string1 = ""
d_string2 = ""
for index in e_array:
for i in range(0, 26):
if index == first_cipher[i]:
d_string1 += alphabet[i]
if index == second_cipher[i]:
d_string2 += alphabet[i]
if upper_flag:
d_string1 = d_string1.upper()
d_string2 = d_string2.upper()
print "first decode method result is:\n"+d_string1
print "second decode method result is:\n"+d_string2
return


if __name__ == '__main__':
print "\t\tcoding by qux"
while True:
print "\t*******Bacon Encode_Decode System*******"
print "input should be only lowercase or uppercase,cipher just include a,b(or A,B)"
print "1.encode\n2.decode\n3.exit"
s_number = raw_input("please input number to choose\n")
if s_number == "1":
encode()
raw_input()
elif s_number == "2":
decode()
raw_input()
elif s_number == "3":
break
else:
continue

签到题ProPlus

解压压缩包,打开,发现里面有一个加密压缩包,和一个Password.txt,打开看一看

于是想到栅栏密码间隔为3凯撒密码间隔为5,先解密英文语句应该是为了判断间隔是否正确。放在线工具上解密,英文语句是Many years later as he faced the firing squad, Colonel Aureliano Buendia was to remember that distant afternoon when his father took him to discover ice.,一看是百年孤独的最著名的一句话,那么说明间隔没问题,于是依葫芦画瓢解密码。

解出来密码是EAVMUBAQHQMVEPDT,拿过去解压压缩包。得到一个OK.txt,里面全是OoK?

Google搜一搜是一种密码,于是找到在线解密工具,解出来是base32

继续解密,解出来一串base64,继续解密,出来一个png

原本我是把base64解密成16进制,然后把16进制数赋复制到010Editor里,这样就出来了一个二维码。其实更简单的解法是什么呢?问了学长才知道,base64本身就可以表示图片,只需要在前面加上前缀data:image/png;base64,加上base64码,在浏览器打开,就可以看到图片了。

把图片放在二维码扫描器里,就可以拿到flag了。

每日推荐

解压压缩包后得到.pcapng文件,那应该就是流量分析题了,用WireShark打开。慢慢寻找,时不时地跟踪TCP流或者HTTP流,看到了在数据传输过程中有一个song.zip,于是想办法把它提取出来,上网搜一搜就有了,看这里

提取出来以后得到加密压缩包song.zip,在学长的提醒下,我用记事本打开它,在文件的末尾看到一行字。

密码是六位数字,那我爆破也许也很快,于是上工具!

得到一个.mp3文件,然后用Audacity打开,看一看频谱图flag就出来了。

后记

我想再去把Cryptoak了。