Skip to content

Commit fb16ea2

Browse files
committed
adding writeups for mindblown, dtune, isolv and pwn
1 parent 5cede93 commit fb16ea2

File tree

5 files changed

+272
-0
lines changed

5 files changed

+272
-0
lines changed

2016-06-04-backdoor-ctf/crypto_mindblown/README.md

+92
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,96 @@
33
###ENG
44
[PL](#pl-version)
55

6+
We are given authentication logic running on the server:
7+
8+
```
9+
var express = require('express');
10+
var app = express();
11+
var port = process.env.PORT || 9898;
12+
var crypto = require('crypto');
13+
var bodyParser = require('body-parser')
14+
var salt = 'somestring';
15+
var iteration = /// some number here;
16+
var keylength = // some number here;
17+
18+
app.post('/login', function (req, res) {
19+
var username = req.body.username;
20+
var password = req.body.password;
21+
if (username !== 'chintu') {
22+
res.send('Username is wrong');
23+
return;
24+
}
25+
if (crypto.pbkdf2Sync(password, salt, iteration, keylength).toString() === hashOfPassword) {
26+
if (password === 'complexPasswordWhichContainsManyCharactersWithRandomSuffixeghjrjg') {
27+
// some logic here and return something
28+
} else {
29+
// return flag here
30+
}
31+
} else {
32+
res.send('Password is wrong');
33+
}
34+
});
35+
```
36+
37+
It seems straightforward: we need to login as user `chintu` and our password hash has to match the hash value for `complexPasswordWhichContainsManyCharactersWithRandomSuffixeghjrjg` while at the same time it has to be a different string.
38+
39+
Initially we thought it will require time-consuming hash collision generation, almost impossible since we don't know the number of iterations and keylen.
40+
41+
However, we found this: https://mathiasbynens.be/notes/pbkdf2-hmac which describes vulnerability of `pbkdf2Sync` function in case password is longer than the size of hash function used.
42+
In our case the default `sha1` hash is used and we notice that the user password is longer than 64 bytes.
43+
This means that `pbkdf2Sync` will actually use `sha1(password)` instead of actual passsword value, and therefore we can use `sha1(password)` as `password` itself -> `sha1('complexPasswordWhichContainsManyCharactersWithRandomSuffixeghjrjg') = e6~n22k81<[p"k5hhV6*`
44+
45+
We therefore login using
46+
`username: chintu`
47+
`password: e6~n22k81<[p"k5hhV6*`
48+
49+
And we get the flag.
50+
651
###PL version
52+
53+
Dostajemy kod autentykacji działającej na serwerze:
54+
55+
```
56+
var express = require('express');
57+
var app = express();
58+
var port = process.env.PORT || 9898;
59+
var crypto = require('crypto');
60+
var bodyParser = require('body-parser')
61+
var salt = 'somestring';
62+
var iteration = /// some number here;
63+
var keylength = // some number here;
64+
65+
app.post('/login', function (req, res) {
66+
var username = req.body.username;
67+
var password = req.body.password;
68+
if (username !== 'chintu') {
69+
res.send('Username is wrong');
70+
return;
71+
}
72+
if (crypto.pbkdf2Sync(password, salt, iteration, keylength).toString() === hashOfPassword) {
73+
if (password === 'complexPasswordWhichContainsManyCharactersWithRandomSuffixeghjrjg') {
74+
// some logic here and return something
75+
} else {
76+
// return flag here
77+
}
78+
} else {
79+
res.send('Password is wrong');
80+
}
81+
});
82+
```
83+
84+
Zadanie wydaje się dość proste koncepcyjnie: mamy zalogować się jako użytkownik `chintu` a hash podanego hasła musi zgodzić się z hashem dla hasła `complexPasswordWhichContainsManyCharactersWithRandomSuffixeghjrjg`, jednocześnie będąc innym stringiem.
85+
86+
Początkowo myśleliśmy, że będzie wymagało to czasochłonnego liczenia kolizji hashy, praktycznie niemożliwego przy braku znajomości liczby iteracji oraz długości klucza.
87+
88+
Niemniej jednak znaleźliśmy artykuł: https://mathiasbynens.be/notes/pbkdf2-hmac opisujący podatność funkcji `pbkdf2Sync` w sytuacji gdy podane hasło jest dłuższe niż rozmiar hasha używanej funkcji.
89+
90+
W naszym przypadku używana jest domyślna funkcja `sha1` i widzimy że hasło ma wiecej niż 64 bajty.
91+
To oznacza, że `pbkdf2Sync` w praktyce użyje wartości `sha1(password)` zamiast wartości hasła a co za tym idzie możemy z powodzeniem użyć wartości `sha1(password)` jako `password` -> `sha1('complexPasswordWhichContainsManyCharactersWithRandomSuffixeghjrjg') = e6~n22k81<[p"k5hhV6*`
92+
93+
94+
Dzięki temu logujemy się za pomocą:
95+
`username: chintu`
96+
`password: e6~n22k81<[p"k5hhV6*`
97+
98+
I dostajemy flagę.
1.09 MB
Binary file not shown.

2016-06-04-backdoor-ctf/misc_dtune/README.md

+18
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,22 @@
33
###ENG
44
[PL](#pl-version)
55

6+
We get an [audio file](Dtune.wav) with recording of someone typing a password.
7+
We recognized the sounds as phone dial tones, and therefore we used an Audacity plugin to recognize which buttons were pressed:
8+
`8 44 33 0 333 555 2 4 0 444 7777 0 7777 44 2 2 5 6 0 666 333 0 33 7 222 3 44 99 44 6 222 2 77 9`
9+
10+
Initially we mistakingly merged the multiplied button presses and we thought we need to use T9 to recognize words.
11+
Only later we figured that it was plain old keyboard typing, which translated to:
12+
13+
`the flag is sha256 of EPCDHXHMCAQW`
14+
615
###PL version
16+
17+
Dostaliśmy [plik audio](Dtune.wav) z nagraniem wpisywania hasła.
18+
Rozpoznalismy dźwięki jako dźwięki wybierania tonowego na klawiaturze telefonicznej i wykorzystaliśmy plugin do Audacity, aby rozpoznać które klawisze wybrano:
19+
`8 44 33 0 333 555 2 4 0 444 7777 0 7777 44 2 2 5 6 0 666 333 0 33 7 222 3 44 99 44 6 222 2 77 9`
20+
21+
Początkowo błędne skleiliśmy te same klawisze i próbowaliśmy dopasować słowa za pomocą T9.
22+
Dopiero później zorientowaliśmy się, że klawisze są wybierane w zwykły sposób i wpisany tekst tłumaczy się do:
23+
24+
`the flag is sha256 of EPCDHXHMCAQW `

2016-06-04-backdoor-ctf/ppc_isolve/README.md

+134
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,138 @@
33
###ENG
44
[PL](#pl-version)
55

6+
The task was pretty obvious: the server provides us with a regular expression and we need to supply a string which can be matched by this regex.
7+
Since we're lazy we checked if someone has not done this already, and of course they did: https://bitbucket.org/leapfrogdevelopment/rstr/
8+
9+
So we simply write a short parser for the communication with server, and use `xeger` to get answers.
10+
We had to modify the xeger code a bit because it was generating `\n` in place of whitespace placeholders and the server was not handling this well, so we forced the library to use `\t` always for `\s`.
11+
There was also some issue with non-alphanumeric characters so we also forced those to a single chosen character.
12+
Rest was just the communication with the server:
13+
14+
```python
15+
import rstr
16+
import socket
17+
import re
18+
from time import sleep
19+
20+
def recvuntil(s, pattern, tryouts):
21+
data = ""
22+
for i in range(tryouts):
23+
sleep(1)
24+
data += s.recv(9999)
25+
if pattern in data:
26+
return data
27+
return data
28+
29+
30+
def main():
31+
url = "hack.bckdr.in"
32+
port = 7070
33+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
34+
s.connect((url, port))
35+
while True:
36+
task = recvuntil(s, "Pass your solution:", 10)
37+
print(task)
38+
to_solve = re.findall("Your regex:\s+(.*)\s+Pass your solution", task)
39+
if len(to_solve) == 0:
40+
print(task)
41+
else:
42+
print("to solve = '%s'" % to_solve[0])
43+
solution = rstr.xeger(to_solve[0])
44+
print("solution = %s" % solution)
45+
s.sendall(solution + "\n")
46+
47+
48+
main()
49+
```
50+
51+
Which gave:
52+
53+
```
54+
Passed regex! Way to go.
55+
################################
56+
####### ROUND 47 #####
57+
################################
58+
59+
60+
Your regex:
61+
ab*
62+
63+
64+
Pass your solution:
65+
66+
to solve = 'ab*'
67+
solution = ab
68+
Passed regex! Way to go.
69+
Congratulations, you can now say ISOLVE
70+
flag{...}
71+
```
72+
673
###PL version
74+
75+
Zadanie było dość oczywiste koncepcyjnie: serwer podaje nam wyrażenie regularne a my mamy zwrócić ciąg znaków, który będzie przez to wyrażenie przyjęty.
76+
Jako, że jesteśmy leniwi z natury, sprawdziliśmy czy ktoś już czegoś podobnego nie napisał i oczywiście ktoś taki się znalazł: https://bitbucket.org/leapfrogdevelopment/rstr/
77+
78+
W związku z tym napisalismy krótki parser do komunikacji z serwerem i użylismy `xegera` do uzyskiwania odpowiedzi.
79+
Musieliśmy lekko zmodyfikować kod biblioteki ponieważ generowała znaki `\n` jako białe znaki, a serwer sobie z tym nie radził zbyt dobrze, w związku z tym wymusiliśmy używanie `\t` zawsze dla `\s`.
80+
Były też jakieś problemy ze znakami nie-alfanumerycznymi i te także sprowadziliśmy do jednego wybranego znaku.
81+
Reszta to już tylko komunikacja z serwerem:
82+
83+
```python
84+
import rstr
85+
import socket
86+
import re
87+
from time import sleep
88+
89+
def recvuntil(s, pattern, tryouts):
90+
data = ""
91+
for i in range(tryouts):
92+
sleep(1)
93+
data += s.recv(9999)
94+
if pattern in data:
95+
return data
96+
return data
97+
98+
99+
def main():
100+
url = "hack.bckdr.in"
101+
port = 7070
102+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
103+
s.connect((url, port))
104+
while True:
105+
task = recvuntil(s, "Pass your solution:", 10)
106+
print(task)
107+
to_solve = re.findall("Your regex:\s+(.*)\s+Pass your solution", task)
108+
if len(to_solve) == 0:
109+
print(task)
110+
else:
111+
print("to solve = '%s'" % to_solve[0])
112+
solution = rstr.xeger(to_solve[0])
113+
print("solution = %s" % solution)
114+
s.sendall(solution + "\n")
115+
116+
117+
main()
118+
```
119+
120+
Co dało:
121+
122+
```
123+
Passed regex! Way to go.
124+
################################
125+
####### ROUND 47 #####
126+
################################
127+
128+
129+
Your regex:
130+
ab*
131+
132+
133+
Pass your solution:
134+
135+
to solve = 'ab*'
136+
solution = ab
137+
Passed regex! Way to go.
138+
Congratulations, you can now say ISOLVE
139+
flag{...}
140+
```

2016-06-04-backdoor-ctf/pwn_worst/README.md

+28
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,32 @@
33
###ENG
44
[PL](#pl-version)
55

6+
We get netcat connection parameters to work with.
7+
After connecting to the server we get a custom command-line prompt, but typing some random things cause the connection to close.
8+
Some fuzzing lead us to typing `1+()` which crashes with a nice python exception that we can't add int to a tuple.
9+
This means that our input has to be evaluated by python interpreter.
10+
We use this to run:
11+
12+
```python
13+
__import__("pty").spawn("/bin/sh")
14+
```
15+
16+
To get a real shell on the target machine.
17+
After that we tried the standard aproach with `find flag` but it gave us nothing.
18+
Fortunately we came back to task description which stated that the admin is `environmentalist` and so we typed `set` to see evn variables and there was a variable named `_F_L_AG` containing the flag itself.
19+
620
###PL version
21+
22+
Dostajemy parametry do połączenia się przez netcata.
23+
Po połączeniu serwer wyświetla znak zachęty jak w shellu, ale próby wpisywania losowych rzeczy powodują zerwanie połączenia.
24+
Trochę fuzzowanai pozwala zauważyć że wpisanie `1+()` wysypuje shella z ładnym pythonowym wyjątkiem ze nie można dodać inta do krotki.
25+
To oznacza, że nasz input jest ewaluowany przez interpreter pythona.
26+
W związku z tym uruchamiamy:
27+
28+
```python
29+
__import__("pty").spawn("/bin/sh")
30+
```
31+
32+
Aby dostać prawdziwego shella na docelowej maszynie.
33+
Po tym próbowaliśmy trochę standardowych operacji jak `find flag` ale niczego nie znaleźliśmy.
34+
Na szczęście wróciliśmy do opisu zadania, w którym było napisane że admin to `environmentalist` w zwiazku z tym wpisaliśmy komendę `set` żeby zobaczyć wszystkie zmienne środowiskowe i faktycznie jedną z nich była `_F_L_AG` zawierająca flagę.

0 commit comments

Comments
 (0)