Xi4or0uji's blog

2019 defcon ctf 学习

字数统计: 1.7k阅读时长: 8 min
2019/05/19 Share

菜鸡web手这场比赛全程挂机……web是真的越来越难打了

cant_even_unplug_it

一题脑洞了2333
题目+hint放一放

1
2
3
4
5
You know, we had this up and everything. Prepped nice HTML5, started deploying on a military-grade-secrets.dev subdomain, got the certificate, the whole shabang. Boss-man got moody and wanted another name, we set up the new names and all. Finally he got scared and unplugged the server. Can you believe it? Unplugged. Like that can keep it secret…

Hint: these are HTTPS sites. Who is publicly and transparently logging the info you need?

Just in case: all info is freely accessible, no subscriptions are necessary. The names cannot really be guessed.

一开始看到这道题目懵逼了,怎么没有网站???经过菜鸡谷歌+半懵半猜终于读懂了题目(wtcl
他们在military-grade-secrets.dev这个子域建了一个网站,但是他们改变了网站名,所以我们首先要将原来的网站名找出来
首先所有的域名和名称更改都通过DNS进行追踪,用https://securitytrails.com/dns-trails 去查看同一个子域下的其他域名,可以看到这个

访问一下secret-storage.military-grade-secrets.dev发现会自动跳转去forget-me-not.even-more-militarygrade.pw这个网站

现在我们再看一下forget-me-not.even-more-militarygrade.pw这个网站,可以看到这里有个DigitalOcean

所以这个网站曾经在Digital Ocean上托管过,接着再去https://archive.org/web/ 去找网站的快照就会找到4月27日有一个

redacted-puzzle

identify分析一波,可以看到只有个别像素变了,其他基本没变

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
redacted-puzzle.gif[0] GIF 1280x720 1280x720+0+0 8-bit sRGB 4c 80KB 0.000u 0:00.019
redacted-puzzle.gif[1] GIF 592x595 1280x720+341+61 8-bit sRGB 4c 80KB 0.000u 0:00.019
redacted-puzzle.gif[2] GIF 592x602 1280x720+341+59 8-bit sRGB 4c 80KB 0.000u 0:00.019
redacted-puzzle.gif[3] GIF 577x608 1280x720+353+56 8-bit sRGB 4c 80KB 0.000u 0:00.019
redacted-puzzle.gif[4] GIF 409x610 1280x720+334+56 8-bit sRGB 4c 80KB 0.000u 0:00.019
redacted-puzzle.gif[5] GIF 616x592 1280x720+332+76 8-bit sRGB 4c 80KB 0.000u 0:00.019
redacted-puzzle.gif[6] GIF 616x619 1280x720+332+49 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[7] GIF 590x584 1280x720+362+49 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[8] GIF 486x586 1280x720+466+47 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[9] GIF 495x630 1280x720+460+45 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[10] GIF 504x630 1280x720+451+45 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[11] GIF 574x581 1280x720+323+96 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[12] GIF 635x635 1280x720+323+42 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[13] GIF 572x637 1280x720+387+42 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[14] GIF 352x287 1280x720+607+392 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[15] GIF 559x559 1280x720+320+121 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[16] GIF 640x640 1280x720+320+40 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[17] GIF 640x640 1280x720+320+40 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[18] GIF 458x640 1280x720+411+40 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[19] GIF 640x549 1280x720+320+131 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[20] GIF 640x559 1280x720+320+121 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[21] GIF 565x559 1280x720+395+121 8-bit sRGB 4c 80KB 0.000u 0:00.009
redacted-puzzle.gif[22] GIF 347x528 1280x720+320+152 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[23] GIF 638x450 1280x720+320+107 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[24] GIF 636x634 1280x720+322+43 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[25] GIF 633x634 1280x720+324+43 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[26] GIF 632x630 1280x720+324+45 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[27] GIF 583x630 1280x720+370+45 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[28] GIF 583x625 1280x720+370+47 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[29] GIF 622x474 1280x720+330+198 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[30] GIF 620x618 1280x720+330+52 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[31] GIF 589x614 1280x720+332+52 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[32] GIF 449x612 1280x720+334+54 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[33] GIF 605x608 1280x720+336+56 8-bit sRGB 4c 80KB 0.000u 0:00.000
redacted-puzzle.gif[34] GIF 600x431 1280x720+341+230 8-bit sRGB 4c 80KB 0.000u 0:00.000

stego看一下有个字母表?

接着identify -verbose redacted-puzzle.gif查看一下图片签名,可以看到black被定义了三次??

1
2
3
4
5
6
7
8
9
Colors: 1
Histogram:
352240: ( 0, 0, 0,255) #000000FF black
Colormap entries: 4
Colormap:
0: ( 0, 0, 0,255) #000000FF black
1: ( 0, 0, 0,255) #000000FF black
2: ( 0, 0, 0,255) #000000FF black
3: (255,255,255, 0) #FFFFFF00 srgba(255,255,255,0)

因此即使底色为黑色,图片依旧能存储,所以其实图片的存储并不是真正的一整张图片的存储的,我们尝试一下修改一下每帧的调色板并且将它们保存成png图片

1
2
3
4
5
6
7
8
9
#!conding: utf-8
from PIL import Image
# 修改底色,保存图片
imageObject = Image.open("./redacted-puzzle.gif")
for frame in range(0,imageObject.n_frames):
imageObject.seek(frame)
imageObject.putpalette([255,0,0,0,0,0,0,0,0])
rgb_im = imageObject.convert('RGB')
rgb_im.save("./tmp/output-%02u.png" % frame)


接下来就是脑洞了,拿到了上面的图片,都是一堆多边形,但是如果我们将它们全部拼在一起可以拿到一个八边形,让我们假设成二进制,就可以拿到一堆二进制,先看第一张图片

所以这个图片的二进制数就是10001100
我们将这些数字都列出来,联系字母表是32位,我们推测试base32变种

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
# 解密
flag_alphabet = "+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}"
flag_bits=[
"10001100",
"01100011",
"11100100",
"01000110",
"10000101",
"00111101",
"01000010",
"10011000",
"11100000",
"11110100",
"10000000",
"00101101",
"01110010",
"00011100",
"00001000",
"10100101",
"11010111",
"01101110",
"10100110",
"10010001",
"10111100",
"10000100",
"10000001",
"10111001",
"11010100",
"00111011",
"11001110",
"11110010",
"00011110",
"10011101",
"11001001",
"11000111",
"01100101",
"00011110",
"10011111",
]

# base32解密
flag_bits = "".join(flag_bits)
result = ""
for i in range(0,len(flag_bits),5):
index = flag_bits[i:i+5]
index = int(index,2)
result += flag_alphabet[index]

print("result: ", result)

所以最后解出来是

nodb

源码有个这个东西

1
2
3
4
5
6
7
8
function validateForm() {
str = document.forms["myForm"]["password"].value
ptr = allocate(intArrayFromString(str), 'i8', ALLOC_NORMAL);
ret = UTF8ToString(_authenticate(ptr));
console.log(ret);
if (ret == "success") document.getElementsByClassName("text")[0].innerText = "SUCCESS"
return false;
}

所以如果一个字符串通过了_authenticate函数的认证而且放回seccess就是正确的flag了
接下来我们找一下_authenticate在哪里出现过
有一个wasm.wasm文件,我们先丢进去反编译一下,因为菜鸡的文件不知道为什么损坏了,所以只能贴上其他大师傅的图了

可以看到,如果要返回success,那就要满足v6==69,但是这一段逆回去很难,代码很复杂,V6是计算这段字母里面有多少个字母是符合password的,因此,我们尝试一下暴力转v6,而且每一次强制匹配一个字母
继续看wasm文件,将它转成wat文件(虽然也不算非常好看,但是还是能看出来的,搜一下1245只出现过一次,分析文件

利用_authenticate返回正确的字数,我们一位位将flag爆出来

CATALOG
  1. 1. cant_even_unplug_it
  2. 2. redacted-puzzle
  3. 3. nodb