La presenza di una richiesta di password è indice di protezione e di accesso controllato; perché allora non impostarne una anche per la shell, che è uno degli strumenti più cruciali per la sicurezza del nostro PC? L’ideale è che si tratti di una password diversa da quella dell’utente, in modo che se l’account viene compromesso, non è comunque automatico poter accedere anche alla shell con la medesima password.
Tutelare l’accesso alla shell risulta utile per inibire malware di tipo fileless o LOTL (Living-Off-The-Land) e ostacolare attacchi di lateral movement o pivoting che abbiano come fulcro PowerShell. Sfortunatamente l’uso di Powershell-empire o altri “PowerShell portable” rende aggirabile la precauzione che andremo ad implementare, che comunque in molti altri casi può fare la differenza fra un PC salvo e uno compromesso.
Prepariamo PowerShell
Sottolineiamo preliminarmente che useremo PowerShell 5.1, già preinstallato nelle moderne versioni di Windows, con la default policy impostata su restricted, ossia l’esecuzione di qualunque script è bloccata. Chi usa PowerShell di tanto in tanto, l’avrà probabilmente già settata su remote signed, ovvero gli script prodotti localmente possono essere eseguiti, ma quelli provenienti da altri PC (o che hanno il “mark of the web”) devono essere digitalmente firmati (e non è sufficiente siano self-signed). Per settare tale impostazione il comando è “Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
” e con “Get-ExecutionPolicy -List
” possiamo vedere l’elenco completo delle policy per i differenti “contesti” (“scope”, ad essere più precisi).
I più attenti alla sicurezza, se hanno Windows in versione Pro, avranno già abilitato nelle group policy (“Criteri Computer Locale”/”Configurazione Computer”/”“Modelli Amministrativi”/“Componenti di Windows”/”Windows PowerShell”/”Attiva l’esecuzione di script”; v. immagine sottostante) una execution policy che non consenta il famigerato bypass nell’esecuzione di script; metodo che potrebbe essere usato in modo nefasto dai malware o utenti troppo spericolati. Nel nostro caso è sufficiente impostare “Consenti script locali e script remoti firmati”, equivalente a “remote signed”.
Nel caso di PowerShell 7, ciò richiede che venga lanciato lo script “InstallPSCorePolicyDefinitions.ps1” contenuto nella cartella d’installazione; il percorso poi da seguire nelle policy sarà “Modelli Amministrativi”/“PowerShell Core”/ “Attiva l’esecuzione di script”. Da notare che la cartella del profilo di PowerShell Core 7 si chiama semplicemente “Powershell”, si trova normalmente al percorso “$HOME\Documents\”.
Impostiamo la password
Una volta settata l’adeguata policy, dovremo verificare che ci sia un profilo PowerShell (di cui abbiamo già parlato qui), usando il comando “Test-Path $PROFILE
”; se il risultato è “False”, allora possiamo provvedere a crearlo con “New-Item -Path $PROFILE -Type File -Force
”. Al percorso di default: “C:\Users\[nome utente]\Documents\WindowsPowerShell” verrà creato il file “Microsoft.PowerShell_profile.ps1” contenente le informazioni e personalizzazioni del profilo. Chiaramente, per depistare eventuali attaccanti, sarebbe meglio non usare il percorso di default e creare il file del profilo altrove, modificando di conseguenza la chiave di registro “HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders” al parametro “Personal”. A questo punto, per impostare la richiesta di password ad ogni avvio di PowerShell agiremo sul file del profilo, come già visto nel succitato articolo. Non resta che inserire alla fine del file il seguente script, che verrà eseguito ad ogni avvio della shell:
[console]::TreatControlCAsInput = $true
$pspwd = "TurboLab"
$pswall = Show-Command Read-Host -PassThru
if ($pswall –eq $null –or $pswall –cnotmatch $pspwd) {
$wshell = New-Object -ComObject wscript.shell
$wshell.AppActivate('Windows PowerShell')
$wshell.SendKeys{"%{F4}"
exit
}
}
Lo script inizialmente disabilita l'"evasione" con la combinazione "Ctrl+C
" (prima riga), poi considera come password valida “TurboLab” (seconda riga), poi legge ciò che viene digitato dall’utente (terza riga) e se non coincide con la password preimpostata (quarta riga) chiude la shell (righe restanti). Come fa l’utente ad avere un campo da compilare, nonostante si trovi in una command line? Questo script, facendo leva su Show-Command
e Read-Host
(seconda riga dello script) ad ogni avvio della shell è in grado di aprire una finestra che richiede l’interazione dell’utente, alla voce “Prompt”. In tale campo vuoto sarà necessario, per chi sa che si tratta di una richiesta di password, inserire quella corretta (“TurboLab” in questo caso) per poter poi iniziare ad usare il terminale.
In pratica, sfruttando la GUI del comando Show-Command, si ottiene una sorta di "CAPTCHA per la shell", che blocca i tentativi di accesso automatizzato che potrebbero essere lanciati da malware, eseguibili malevoli o utenti remoti con cattive intenzioni (in seguito vedremo i dettagli in merito).
Da considerare che, dopo aver abilitato il terminale con la password, il valore delle variabili coinvolte nel suddetto script ($pspwd e $pswall), risulta già settato per la sessione in corso, quindi se lo ritenete opportuno cambiate i nomi delle variabili con nomi che difficilmente pensate possano creare problemi di omonimia (sebbene modificare il valore delle variabili già utilizzare non comprometta alcuna funzionalità, né dello script salvato nel profilo né della shell).
Proteggiamo la password
Se volete essere maggiormente sicuri che non sia facile interferire con la richiesta di password, modificando, rinominando o cancellando il file del profilo che la contiene, non resta che aprire una shell da amministratore e, raggiunta la cartella con il profilo, usare il comando “attrib WindowsPowerShell +h +s +r
“. La cartella risulterà nascosta ("+h"), di sistema ("+s") e di sola lettura ("+r"); per riaverla visibile serve il comando “attrib WindowsPowerShell -h -s -r
”, sempre da shell con diritti di admin. Pur senza sapere il nome esatto della cartella, ci sono comandi per individuare e ripristinare la cartella nascosta (in CMD: “dir /a:h /b /s
” o in PowerShell: “attrib -h -s -r /s /d [percorso]\*.*
”); tuttavia, se a questo stratagemma aggiungiamo l’averla posizionata in un percorso non di default, abbiamo quantomeno reso un po’ più difficile il disinnesco dello script che gestisce la password della shell.
Un’altra procedura per migliorare la sicurezza della cartella del profilo è la seguente: cliccando con il destro sulla cartella che contiene il profilo, in “Proprietà”, alla scheda “Sicurezza”, bisogna accedere ad “Avanzate”, cliccare su “Disabilita ereditarietà” e “Converti autorizzazioni ereditate in autorizzazioni esplicite per questo oggetto”. Selezioniamo l’utente attuale, poi “Modifica”, poi “Mostra autorizzazioni avanzate” e impostiamo il “Tipo” su “Consenti”, selezionando le spunte indicate qui di seguito:
Dopo aver confermato con “OK”, rientriamo in “Modifica” e impostiamo il “Tipo” su “Nega”, quindi, per sicurezza, mettiamo le spunte inverse (tralasciando "Controllo completo"):
A questo punto, la cartella non può essere cancellata e per accedere al file del profilo al suo interno è necessario immettere la password da amministratore. Purtroppo, in caso di interazione malevola umana (quindi non automatizzata con malware o script), può tuttavia essere rinominata, lasciando così PowerShell senza le impostazioni del profilo settate in precedenza.
La password impostata grazie al profilo PowerShell non è dunque una protezione inaggirabile, ma si tratta di un campanello d’allarme, un “alert” di potenziamento da affiancare ai vari execution policy, UAC (User Account Controll), ConstrainedLanguage mode, restrizione di autorizzazioni, AppLocker, group policy, etc. e pur non costituendo una panacea infallibile per ogni reverse shell o bind shell, quantomeno dovrebbe rendere possibile, in qualche caso, potersi accorgere, grazie alla comparsa della finestra della richiesta di password, che qualcosa di losco sta accadendo, anche fosse solo in background. Soprattutto se è un malware a provare a usare PowerShell in automatico, il blocco tramite password dovrebbe risultargli invalicabile, come di fatto accade anche se si prova a lanciare il noto script in python Hoaxshell (che verrebbe comunque bloccato da Defender) e che è una delle reverse shell suggerite, a scopo didattico e puramente illustrativo, qui, tutte destinate a incagliarsi inesorabilmente nella richiesta di password.
Come già accennato, se invece si tratta di un attaccante umano in locale, costui probabilmente innescherà suo malgrado la richiesta di password, ma potrebbe poi intuire che è necessario andare a modificare il profilo utente di PowerShell per acquisire libertà d’azione sulla shell. Quindi è fondamentale, ripetiamolo, che il profilo di PowerShell non sia accessibile a tutti gli utenti né prevedibilmente nel percorso di default, complicando dunque l’attacco, soprattutto se questo non è attrezzato anche per una privilege escalation.
Una ulteriore precauzione difensiva è rendere lo script che gestisce la password meno limpido, offuscando un po’ il testo; l’esempio più scontato (quindi meno utile) potrebbe essere di non scrivere come password, ad esempio, “Ale-075!” bensì "41,6c,65,2d,30,37,35,21", convertendo ogni carattere nel suo corrispettivo in esadecimale, per poi chiedere allo script di riconvertirlo e confrontarlo con l’input dato dall’utente. Il codice diventerebbe allora:
$pspwdx = "41,6c,65,2d,30,37,35,21"
$pspwd = (($pspwdx.Split(",") | % {[char]([convert]::toint16($_,16))})) –join ""
$pswall = Show-Command Read-Host -PassThru
if ($pswall –eq $null –or $pswall –cnotmatch $pspwd) {
$wshell = New-Object -ComObject wscript.shell
$wshell.AppActivate('Windows PowerShell')
$wshell.SendKeys{"%{F4}"}
}
Chiaramente, se l'attaccante riesce a scovare il profilo, l’aver offuscato la password, non scrivendola in chiaro, costituirà solo un mero rallentamento nell’attacco, non certo una soluzione definitiva (sempre che costui non riesca ad acquisire i privilegi per cancellare semplicemente il file del profilo).
Password dinamica
Se sappiamo che può capitare di dover scrivere la password vicino ad occhi indiscreti, considerando che la password viene scritta visibilmente in chiaro, può essere opportuno impostare una password dinamica, ossia che contenga al suo interno una stringa che cambia secondo criteri che solo noi conosciamo. Come esempio intuitivo e banale, potrebbe contenere i due numeri che indicano l’ora attuale moltiplicata 2 (oppure altri calcoli non troppo impegnativi da fare a mente). Per avere come password “Ale-075[ora attuale x 2]”Le prime due righe dello script diventano allora:
$pspwdx="41,6c,65,2d,30,37,35"
$pspwd= ((($pspwdx.Split(",") |% {[char]([convert]::toint16($_,16))})) -join"")+((date).Hour)*2
Da tenere presente che se si usa tale password nell’ora dopo mezzanotte, il numero dinamico è “0”, non “00”, così come alle 4 di notte il numero dinamico sarà “8” non “08”; viene quindi eliminato lo 0 iniziale anche se il doppio dell’ora è un numero a cifra singola.
Ovviamente possiamo aumentare l’intensità dell’offuscamento complicando ulteriormente il codice; ad esempio, al posto di scrivere come password nello script “please...[ora corrente]L0L”, (ossia “please...11L0L” se l’orario del PC è al momento compreso fra le 11:00 e le 11:59) potremmo mischiare ulteriormente le carte in tavola scrivendo:
$pspwdx = "61,6c,65,30,37,35" ; ((((variable "*ted*").name[2,12,1,13,10])+(($pspwdx.Split(",") | % {[char]([convert]::toint16($_,16))})+((date).Hour))+(((variable "*undP*").Value).ToString()[6,17,28]))[4,6,7,5,0,2,12,13,14,11,1,8,1]) -join ""
che, senza entrare nel dettaglio, è facilmente "deoffuscabile" in PowerShell (basta inserirlo nel terminale e premere invio). Tuttavia l’attaccante, se anche arrivasse a trovarsi di fronte tale codice, dovrà per forza usare la shell di un'altra macchina per decifrarlo e difficilmente potrà ricorrere con successo ad altri sistemi operativi (Linux ad esempio) poiché, come i più attenti avranno notato, ci sono alcuni caratteri prelevati da una variabile di sistema Windows, individuata da una parte del suo nome (“*ted*
”). Questo era solo un esempio per suggerire di non scrivere troppo in chiaro la propria password, pur se il file del profilo è tendenzialmente nascosto.
Considerazioni e limitazioni
A questo punto è lecito chiedersi: cosa fare quando compare una imprevista richiesta di password per PowerShell mentre stiamo tranquillamente usando il PC? Per reperire informazioni in caso di lancio sospetto di PowerShell ricordiamo che è possibile usare il programma WinSpy o, ancora meglio, Process Explorer della suite Sysinternals, per verificare quale processo sta invocando PowerShell.
Da tenere presente che all’avvio del PC o durante l’installazione di alcuni aggiornamenti (plausibilmente del sistema operativo) è normale che PowerShell possa venire avviato in background e dunque non sorprendiamoci se in quei casi vediamo comparire un paio di richieste consecutive di sblocco con password. Questo può accadere anche quando alcuni software usano PowerShell per aggiornamenti, come nel seguente caso di AMD:
Process Explorer ci spiega che RadeonSoftware.exe (pertinente la scheda grafica) ha lanciato PowerShell (v. “prefisso” del comando “powershell.exe /C”) nella directory “C:\ProgramFiles\AMD\CNext\CNext” per eseguire il comando “Get-AppPackage | Select Name”, che elenca i nomi dei pacchetti installati nella cartella.
Da segnalare inoltre che, qualora si esegua un tentativo locale di lanciare comandi da CMD volti a eseguire cmdlet in PowerShell ("PowerShell /C [cmdlet]
"), la richiesta di password vigila inesorabilmente sull’accesso a PowerShell, chiudendolo in caso di password errata o tentativo di chiusura della finestra con campo da compilare. Viceversa, anche se PowerShell ha già una finestra aperta in locale, è sufficiente passare a CMD, digitando l’omonimo comando nella shell, per essere poi costretti a reimmettere la password quando si torna alla esecuzione di PowerShell nella medesima finestra.
C’è almeno un modo per aggirare tale richiesta di password? Sì e non è esattamente impossibile da indovinare, per un utente umano; quindi, se lo trovate, scrivetelo pure nei commenti.
E in caso di tentativi di connessione da remoto? Il terminale di PowerShell rimane inaccessibile senza inserimento password, sebbene vada ricordato che ad esempio nelle connessioni SSH viene usato CMD e PowerShell non viene quindi coinvolto direttamente. Nondimeno, se dalla shell SSH si prova a passare a PowerShell (o lanciare un cmdlet usando il suddetto “PowerShell /C"), la shell sembra andare in stallo, non potendo visualizzare da remoto la richiesta di password. Comunque è sufficiente non essere rinunciatari e… anche qui c’è almeno un modo abbastanza semplice ed intuitivo (molto simile al precedente) per bypassare la richiesta di password; potete scrivere anche questo nei commenti.
Infine, ricordate che per disabilitare la richiesta di password, è sufficiente rinominare, spostare o cancellare il file del profilo oppure, se volete tenerlo perché contiene altre impostazioni personalizzate, basta includere lo script della password fra “<#” e “#>” in modo che risulti solo un commento e non venga eseguito.