2020 Defenit CTF Write-up 6/5 09:00 ~ 6/7 09:00 (UTC, 48h)
Forensic
Baby Steganography
I heared you can find hide data in Audio Sub Bit. Do you want to look for it?
.wav 파일이 주어진다. 문제 지문을 보면 Audio Sub Bit에 데이터를 숨긴다고 한다.
Audio Sub Bit...
Sub Bit...
SB...
LSB?
LSB가 아닐까 생각을 해보고서 바로 구글링을 했다.
Audio Steganography : LSB
포스트를 내리다보면 Python으로 작성된 Decode 코드가 있다. 바로 긁어다가 실행해 보았다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# Use wave package (native to Python) for reading the received audio file
import wave
song = wave.open("problem", mode='rb')
# Convert audio to byte array
frame_bytes = bytearray(list(song.readframes(song.getnframes())))
# Extract the LSB of each byte
extracted = [frame_bytes[i] & 1 for i in range(len(frame_bytes))]
# Convert byte array back to string
string = "".join(chr(int("".join(map(str,extracted[i:i+8])),2)) for i in range(0,len(extracted),8))
# Cut off at the filler characters
decoded = string.split("###")[0]
# Print the extracted text
print("Sucessfully decoded: "+decoded)
song.close()
|
cs |
Sucessfully decoded: Defenit{Y0u_knOw_tH3_@uD10_5t39@No9rAphy?!}
결과는 대성공.
Misc
QR Generator
Escape from QR devil!
nc 연결을 하자.
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
|
Let's START!
< QR >
1 1 1 1 1 1 1 0 1 1 0 0 0 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1
1 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1
1 0 1 1 1 0 1 0 1 0 1 0 1 1 1 0 1 0 0 1 1 0 1 0 1 1 1 0 1
1 0 1 1 1 0 1 0 1 1 0 0 1 1 0 1 1 0 1 0 1 0 1 0 1 1 1 0 1
1 0 1 1 1 0 1 0 1 0 1 0 1 0 0 1 0 1 0 0 1 0 1 0 1 1 1 0 1
1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1
1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 1 1 1 0 1 1 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0
0 1 0 1 0 1 1 0 1 0 0 1 0 1 1 0 0 0 1 1 0 1 1 0 1 1 1 1 1
0 1 1 1 1 1 0 1 1 0 1 1 1 1 0 0 0 0 1 1 0 1 0 0 1 1 0 1 1
0 1 0 1 0 1 1 1 1 0 1 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0 0 0 1
1 0 1 0 1 0 0 1 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 1
0 0 0 0 1 0 1 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1
1 1 0 1 0 1 0 0 1 0 1 0 0 0 0 0 1 1 1 0 1 0 1 0 1 0 1 0 1
0 1 1 0 1 0 1 1 1 1 1 1 1 0 1 0 1 1 0 0 1 0 0 1 0 1 0 1 1
0 1 1 1 1 1 0 1 1 1 1 0 1 1 1 0 1 1 1 1 1 0 1 0 1 0 1 0 1
1 0 0 1 1 0 1 1 1 1 1 0 0 1 1 0 0 1 0 1 1 0 0 1 1 1 0 0 1
1 0 0 1 0 0 0 0 1 1 0 0 1 0 0 0 1 0 0 0 1 0 1 0 0 1 0 0 0
1 1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1 0 1 1 0 1 1 0 0 0 1 1 0
0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 0 0 0 1 0 1 0 1 1
1 0 0 0 1 0 1 0 1 1 1 1 1 0 0 1 0 1 1 0 1 1 1 1 1 0 0 1 0
0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0
1 1 1 1 1 1 1 0 0 0 1 0 0 1 1 0 0 0 0 1 1 0 1 0 1 0 0 0 1
1 0 0 0 0 0 1 0 1 1 1 1 0 1 0 1 1 1 0 0 1 0 0 0 1 1 1 1 0
1 0 1 1 1 0 1 0 0 1 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0
1 0 1 1 1 0 1 0 1 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 0 1 1 1 0
1 0 1 1 1 0 1 0 0 0 1 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0
1 0 0 0 0 0 1 0 1 0 0 0 0 1 0 1 0 0 0 0 1 1 0 0 0 0 1 1 0
1 1 1 1 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0
STAGE 1
>> Time Over!
|
cs |
1과 0으로 된 qrcode를 보내준다.
하얀배경 이미지를 불러와서 값이 1이면 해당 위치의 픽셀의 값을 검정으로 바꾸도록 코드를 쓱-싹 작성해 준다.
하얀배경 이미지는 포토샵을 이용한다. 넉넉하게 200px*200px
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
|
from pwn import *
from PIL import Image
p = remote("qr-generator.ctf.defenit.kr", 9000)
p.recvuntil('name?')
p.sendline('M4ndU')
img = Image.open('./200.png') #load white background 200*200 image
img = img.convert("RGB")
newData = []
qrbin = [0] * 200
p.recvuntil('< QR >\n')
for i in range(200):
data = str(p.recvline())
data = data.replace("b'", "")
data = data.replace("\\n'", "")
data = data.replace(" ", "")
if data == "":
print(p.recvline())
break
qrbin[i] = data
for x in range(len(qrbin)):
if qrbin[x] == 0:
break
for y in range(len(str(qrbin[x]))):
if qrbin[x][y] == "1":
img.putpixel( (x, y), (0, 0, 0)) #black
img.save("./dimg/TransparentImage"+str(n)+".png", "PNG")
p.interactive()
|
cs |
그러면 이러한 qrcode 이미지를 만들어 낼 수 있다.
(제공되는 qrcode 크기가 제각각 이어서 배경 이미지의 크기에 여유를 주었다. 인식하는데에는 문제가 없었다.)
이제 이 qrcode를 decoding해서 보내주기만 하면 된다. 그러나 이 과정에서 삽질이 있었다.
이 qrcode이미지를 디코딩하는 방법에는 여러가지가 있다.
qrcode를 디코딩해주는 사이트에 리퀘스트 날려서 크롤링하는 방법. (이미지 보내고 크롤링 해야되는 무조건 버리고)
파이썬 모듈을 이용해서 디코딩 하는 방법.
qrcode를 디코딩할 수 있는 파이썬 모듈에는 여러가지가 있었다.
먼저 qrtools.
$sudo pip3 install qrtools
qr = qrtools.QR()
AttributeError: module 'qrtools' has no attribute 'QR'
음? (오류 1스택)
파이썬 2로 다시시도..
$sudo pip install qrtools
import qrtools
ImportError: No module named qrtools
??????? (오류 2스택)
다음 시도한 것이 qrcode
d = qrcode.Decoder()
AttributeError: module 'qrcode' has no attribute 'Decoder'
문서에서는 이렇게 쓰라고 나와있었는데 뭐지..(오류 3스택)
pyzbar를 사용해볼까
>>> decode(img)
[]
도대체 되는게 뭡니까(오류 4스택)
$ sudo pip3 install pyqrcode
$ sudo apt-get install python-qrtools
>>> import pyqrcode
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named pyqrcode
(오류 5스택)
>>> import qrtools
>>> qr = qrtools.QR()
>>> qr.decode("./TransparentImage.png")
False
(오류 6스택)
AttributeError: module 'pyqrcode' has no attribute 'Decoder'
(오류 7스택)
...
웹으로 해결할까 생각이 들어서 테스트때 디코딩에 성공했던 ZXng Decode Online에 들어가봤다.
https://zxing.org/w/decode.jspx
하단 내용을 보면 오픈소스 프로젝트라고 써있다.
혹시 파이썬 모듈도 있나 들어가봤다.
pyzxing | Python wrapper to ZXing library |
있다.
바로 설치.
>>> from pyzxing import BarCodeReader
>>> reader = BarCodeReader()
>>> results = reader.decode( './TransparentImage.png')
>>> results
[{'filename': 'file:///home/mandu/Desktop/p/./TransparentImage.png', 'format': 'QR_CODE', 'type': 'TEXT', 'raw': '0pXenF4L00pza6n9XO45WjUvM4RvBUuoQICQAraBTh1rJIBeYUpxxJFNFsGOQ7DFNZ67QzuP0fNzl8of6j3wfObKWkRuUO1xrn2iMLQl7KCBJ6waeBboITBQRPDYtZUmQmHHa85ll4J', 'parsed': '0pXenF4L00pza6n9XO45WjUvM4RvBUuoQICQAraBTh1rJIBeYUpxxJFNFsGOQ7DFNZ67QzuP0fNzl8of6j3wfObKWkRuUO1xrn2iMLQl7KCBJ6waeBboITBQRPDYtZUmQmHHa85ll4J', 'points': [(3.5, 49.5), (3.5, 3.5), (49.5, 3.5), (46.5, 46.5)]}]
>>> results[0]['raw']
'0pXenF4L00pza6n9XO45WjUvM4RvBUuoQICQAraBTh1rJIBeYUpxxJFNFsGOQ7DFNZ67QzuP0fNzl8of6j3wfObKWkRuUO1xrn2iMLQl7KCBJ6waeBboITBQRPDYtZUmQmHHa85ll4J'
성공.
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
|
from pwn import *
from PIL import Image
from pyzxing import BarCodeReader
p = remote("qr-generator.ctf.defenit.kr", 9000)
p.recvuntil('name?')
p.sendline('M4ndU')
for n in range(100):
img = Image.open('./200.png')
img = img.convert("RGB")
newData = []
qrbin = [0] * 200
p.recvuntil('< QR >\n')
for i in range(200):
data = str(p.recvline())
data = data.replace("b'", "")
data = data.replace("\\n'", "")
data = data.replace(" ", "")
if data == "":
print(p.recvline())
break
qrbin[i] = data
for x in range(len(qrbin)):
if qrbin[x] == 0:
break
for y in range(len(str(qrbin[x]))):
if qrbin[x][y] == "1":
img.putpixel( (x, y), (0, 0, 0))
img.save("./dimg/TransparentImage"+str(n)+".png", "PNG")
reader = BarCodeReader()
results = reader.decode('./dimg/TransparentImage'+str(n)+'.png')
print(results[0]['raw'])
p.sendline(results[0]['raw'])
p.interactive()
|
cs |
Minesweeper
Can you solve the minesweeper in one minute?
1분안에 지뢰찾기를 성공하면 되는 문제.
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
|
a b c d e f g h i j k l m n o p
-----------------------------------------------------------------
1 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
2 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
3 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
4 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
5 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
6 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
7 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
8 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
9 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
10 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
11 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
12 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
13 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
14 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
15 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
16 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
Type the column followed by the row (eg. a5). To put or remove a flag, add 'f' to the cell (eg. a5f). Type 'help' to show this message again.
Enter the cell (40 mines left):
|
cs |
이런식으로 칸이 주어지고, a1을 입력하면
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
|
a b c d e f g h i j k l m n o p
-----------------------------------------------------------------
1 | 0 | 0 | 0 | 0 | 0 | 1 | | | | | | | | | | |
-----------------------------------------------------------------
2 | 0 | 0 | 0 | 0 | 0 | 1 | | | | | | | | | | |
-----------------------------------------------------------------
3 | 0 | 1 | 2 | 2 | 1 | 1 | | | | | | | | | | |
-----------------------------------------------------------------
4 | 0 | 1 | | | | | | | | | | | | | | |
-----------------------------------------------------------------
5 | 0 | 1 | | | | | | | | | | | | | | |
-----------------------------------------------------------------
6 | 1 | 1 | | | | | | | | | | | | | | |
-----------------------------------------------------------------
7 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
8 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
9 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
10 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
11 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
12 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
13 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
14 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
15 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
16 | | | | | | | | | | | | | | | | |
-----------------------------------------------------------------
|
cs |
주변 3*3범위에 폭탄의 개수를 알려준다.
지뢰의 위치에는 깃발을 꽂아야 한다. 저기서는 C4자리에 폭탄이 있으니 c4f를 입력해주어야 한다.
다음으로 지뢰가 없는 c5를 입력하여 나아가도록 한다.
이 과정을 자동화 하자.
http://www.hakank.org/numberjack/minesweeper.py
구글링 해서 찾아낸 지뢰찾기 솔버.
사용을 위해서 Numberjack 모듈을 설치해준다.
https://github.com/eomahony/Numberjack
X = -1
default_game = [
[2,3,X,2,2,X,2,1],
[X,X,4,X,X,4,X,2],
[X,X,X,X,X,X,4,X],
[X,5,X,6,X,X,X,2],
[2,X,X,X,5,5,X,2],
[1,3,4,X,X,X,4,X],
[0,1,X,4,X,X,X,3],
[0,1,2,X,2,3,X,2]
]
이렇게 지뢰찾기 배열을 넘겨주면
0 0 1 0 0 1 0 0
1 1 0 0 1 0 1 0
0 0 1 1 0 1 0 1
1 0 1 0 1 1 0 0
0 1 1 1 0 0 1 0
0 0 0 0 1 1 0 1
0 0 1 0 1 0 1 0
0 0 0 1 0 0 1 0
Nodes: 3 Time: 0.0
0: 지뢰없음 1: 지뢰있음 으로 결과를 출력해준다. 확실하지 않은 곳은 확률을 반환해준다. 이것을 개조했다.
문제에 아무 위치 한곳을 입력해주고 그 출력값을 위 코드로 분석하여 확실한 지뢰가 아닌 곳(0)과 지뢰인 곳(1)을 구해낼 수 있다. 지뢰인 곳에는 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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
#!/usr/bin/python
"""
This Numberjack model was created by
Hakan Kjellerstrand (hakank@bonetmail.com)
See also my Numberjack page http://www.hakank.org/numberjack/
"""
import sys
from Numberjack import *
from pwn import *
colNum = "abcdefghijklmnop"
X = -1
alreadyInput = [[0 for ccccc in range(16)] for ddddd in range(16)]
row = [['0' for cola in range(16)] for rowa in range(16)]
gamerow = [[-1 for colas in range(16)] for rowas in range(16)]
p = remote("minesweeper.ctf.defenit.kr", 3333)
p.recvuntil(':')
p.sendline('h7') #중앙을 파면 한 번에 많은 위치를 분석할 수 있지 않을까
default_game = gamerow
default_r = 16
default_c = 16
#
# Solve the Minesweeper problem
#
def minesweeper(libs, game="", r="", c=""):
# Set default problem
if game == "":
game = default_game
r = default_r
c = default_c
else:
print "rows:", r, " cols:", c
#
# Decision variables
# Note: Matrix is defined with cols,rows,...
#
mines = Matrix(c,r,0,1)
S = [-1,0,1] # for the neighbors of this cell
model = Model()
for i in range(r):
for j in range(c):
if game[i][j] >= 0:
model.add(mines[i,j] == 0)
# this cell is the sum of all the surrounding cells
model.add(
game[i][j] == Sum([mines[i+a,j+b]
for a in S for b in S
if i+a>=0 and
j+b>=0 and
i+a<r and
j+b<c
])
)
if game[i][j] > X:
# This cell cannot be a mine
model.add(mines[i,j] == 0)
# print model
for library in libs:
solver = model.load(library)
# print solver
print ''
if solver.solve():
solver.printStatistics()
print_mines(mines, r, c)
print 'Nodes:', solver.getNodes(), ' Time:', solver.getTime()
#while library == 'Mistral' and solver.getNextSolution():
# print_mines(mines, r, c)
# print 'Nodes:', solver.getNodes(), ' Time:', solver.getTime()
else:
print "No solution"
p.interactive()
print ''
#
# Print the mines
#
def print_mines(mines, rows, cols):
for i in range(rows):
print i+1,
for j in range(cols): #이부분을 수정했다. 불확실한 곳은 ?표시
if len(str(mines[i,j])) > 4:
print "?",
else : #확실
print str(mines[i,j]),
if str(mines[i,j]) == '1': #지뢰는 플래그를 세우자
if alreadyInput[i][j] == 0: #중복 입력 방지
print(colNum[j]+str(i+1)+"f"+'\n')
try:
p.send(colNum[j]+str(i+1)+"f"+'\n')
p.recvuntil("):")
except Exception as e:
p.interactive()
elif str(mines[i,j]) == '0': #지뢰가 아니면 주변을 더 알아내자
if alreadyInput[i][j] == 0: #중복 입력 방지
print(colNum[j]+str(i+1)+'\n')
try:
p.send(colNum[j]+str(i+1)+'\n')
p.recvuntil("):")
except Exception as e:
p.interactive()
print ''
def print_game(game, rows, cols):
for i in range(rows):
print i+1,
for j in range(cols):
if game[i][j] == -1:
print "?", #모르거나 지뢰인 곳은 ?표시
else :
print game[i][j],
print ''
# file = "minesweeper1.txt"
for aa in range(6): #루틴을 몇번 돌릴 것인지. 끝까지 돌리는게 최고지만 정확성이 떨어져서 적당히 맞춰줘야 했다.
row = [['0' for cola in range(16)] for rowa in range(16)]
gamerow = [[-1 for colas in range(16)] for rowas in range(16)]
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
for i in range(16):
data = p.recvline()
row[i] = str(data).split('|')
p.recvline()
for j in range(1, 17):
try:
if row[i][j] == ' ':
gamerow[i][j-1] = -1;
elif row[i][j] == ' F ':
alreadyInput[i][j-1] = -1 #중복입력을 방지하기 위함. 깃발을 세웠다는건 여기를 더 이상 이 위치를 입력할 필요가 없다는 것
else :
gamerow[i][j-1] = int(row[i][j].strip());
alreadyInput[i][j-1] = 1 #중복입력ㅇ르 방지하기 위함. 이곳의 값이 있다는건 더 이상 이 위치를 입력할 필요가 없다는
except Exception as e:
p.interactive()
default_game = gamerow
print_game(default_game, 16, 16)
# minesweeper(['NumberjackSolver', 'Mistral'])
minesweeper(['Mistral'])
# minesweeper(['SCIP'])
print "================================================="
p.sendline("h7")
p.interactive()
|
cs |
근데 solver가 완벽하지는 않다. 완벽한 지뢰 위치를 찾아주지 못한다. 그래서 중간에 game over가 나기도 한다.
그래서 약 6번의 루틴을 시도하게 하고, 코드를 수십번 돌려서 운좋게 165행 코드가 실행이 되면 약 3칸정도가 남는다. 이건 눈으로 보고 풀어서 flag를 받을 수 있었다.
It took you 0 minutes and 17 seconds.
You Win!!!
Defenit{min35w33p3r_i5_ezpz}
'CTF Write Up' 카테고리의 다른 글
CCE2020 Quals Write-up (0) | 2020.09.26 |
---|---|
FIESTA 2020 Write up (2) | 2020.09.07 |
angstrom CTF 2020 write up (0) | 2020.03.14 |
UTCTF 2020 Write up (0) | 2020.03.07 |
RiceTeaCatPanda CTF 2020 Write up (0) | 2020.01.22 |