CVE-2025-69460 – Simple Image Gallery 1.0 - Remote Code Execution (Unauthenticated)
Wed Jan 21 2026 · 3 min read
Category: Security Research
Introduction
In this report, I will detail the discovery and exploitation of a critical Remote Code Execution (RCE) vulnerability in the "Simple Image Gallery 1.0" application. As a security researcher, my goal was to achieve full control over the server via an unauthenticated attack vector. The attack chain consists of two main vulnerabilities: SQL Injection to bypass authentication and Unrestricted File Upload to execute arbitrary code.
Vulnerability Analysis
Phase 1: Authentication Bypass (SQL Injection)
My first target was the application's login mechanism. Upon analyzing classes/Login.php, I noticed that user inputs were directly concatenated into the database query.
// Vulnerable Code in classes/Login.php
$qry = $this->conn->query("SELECT * from users where username = '$username' and password = md5('$password') ");
As seen, the $username variable is inserted into the SQL query without any sanitization. This allows an attacker to manipulate the query logic.
Payload: admin' or '1'='1'#
When this payload is used, 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 the 1=1 condition is always true, the database returns the first user (usually the administrator). Thus, access to the system is granted without a password.
Phase 2: Remote Code Execution (Unrestricted File Upload)
After gaining administrative access, I examined the file upload functions. During the user profile update process (classes/Users.php), I found that uploaded files were not sufficiently validated.
// 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 vulnerabilities here are:
- No Extension Validation: The code uses the filename (
$_FILES['img']['name']) directly and does not check if the extension is safe (e.g., only.jpgor.png). - No Content Check: The file content or MIME type is not verified.
- Executable File: When a file with a
.phpextension is uploaded, the server treats it as a PHP script and executes it.
This vulnerability allows an attacker to upload a malicious PHP file (web shell) to the server and execute commands by calling this file via the browser.
Exploit Development
I developed a Python script (exploit.py) to automate this attack chain. The logic of the script is as follows:
- Session Initialization: An HTTP session is started using
requests.session(). - Login Bypass: An SQL injection payload (
admin' or '1'='1'#) is sent toclasses/Login.php?f=login. Upon a successful response, the session is authenticated. - Protecting User Data: Since the profile update process replaces all fields, the current user details (ID, Firstname, Lastname, Username) are first scraped from the
?page=userpage. This step is crucial to prevent data corruption. - Shell Upload: A
multipart/form-datarequest is sent toclasses/Users.php?f=save.- The scraped user details are added as form data.
- A randomly named file containing PHP code and having a
.phpextension is added to theimgfield. - Payload:
<?php if(isset($_GET['cmd'])){ echo '<pre>'; $cmd = ($_GET['cmd']); system($cmd); echo '</pre>'; die; } ?>
- Execution: After the upload, the profile page is checked again to find the path (
srcattribute) of the uploaded file. Finally, a command execution test is performed by appending?cmd=whoamito this path.
Remediation
To fix these vulnerabilities, the following steps should be taken:
1. Prevent SQL Injection:
Use Prepared Statements instead of string concatenation in database queries.
Secure Example:
$stmt = $this->conn->prepare("SELECT * from users where username = ? and password = md5(?)");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
2. Secure File Upload:
File extensions and types must be strictly validated. Filenames should not be taken from the user but should be randomly generated by the server.
Secure Example:
$allowed = array('jpg', 'jpeg', 'png', 'gif');
$ext = pathinfo($_FILES['img']['name'], PATHINFO_EXTENSION);
if(!in_array(strtolower($ext), $allowed)) {
exit("Invalid file type");
}
$fname = 'uploads/' . uniqid() . '.' . $ext;
CVE ID: CVE-2025-69460
Original public disclosure: August 17, 2021
Researcher: Tağmaç "Tagoletta"
Canonical reference: https://www.exploit-db.com/exploits/50214
Github Repository: https://github.com/Tagoletta/CVE-2025-69460
Introduction
In this report, I will detail the discovery and exploitation of a critical Remote Code Execution (RCE) vulnerability in the "Simple Image Gallery 1.0" application. As a security researcher, my goal was to achieve full control over the server via an unauthenticated attack vector. The attack chain consists of two main vulnerabilities: SQL Injection to bypass authentication and Unrestricted File Upload to execute arbitrary code.
Vulnerability Analysis
Phase 1: Authentication Bypass (SQL Injection)
My first target was the application's login mechanism. Upon analyzing classes/Login.php, I noticed that user inputs were directly concatenated into the database query.
// Vulnerable Code in classes/Login.php
$qry = $this->conn->query("SELECT * from users where username = '$username' and password = md5('$password') ");
As seen, the $username variable is inserted into the SQL query without any sanitization. This allows an attacker to manipulate the query logic.
Payload: admin' or '1'='1'#
When this payload is used, 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 the 1=1 condition is always true, the database returns the first user (usually the administrator). Thus, access to the system is granted without a password.
Phase 2: Remote Code Execution (Unrestricted File Upload)
After gaining administrative access, I examined the file upload functions. During the user profile update process (classes/Users.php), I found that uploaded files were not sufficiently validated.
// 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 vulnerabilities here are:
- No Extension Validation: The code uses the filename (
$_FILES['img']['name']) directly and does not check if the extension is safe (e.g., only.jpgor.png). - No Content Check: The file content or MIME type is not verified.
- Executable File: When a file with a
.phpextension is uploaded, the server treats it as a PHP script and executes it.
This vulnerability allows an attacker to upload a malicious PHP file (web shell) to the server and execute commands by calling this file via the browser.
Exploit Development
I developed a Python script (exploit.py) to automate this attack chain. The logic of the script is as follows:
- Session Initialization: An HTTP session is started using
requests.session(). - Login Bypass: An SQL injection payload (
admin' or '1'='1'#) is sent toclasses/Login.php?f=login. Upon a successful response, the session is authenticated. - Protecting User Data: Since the profile update process replaces all fields, the current user details (ID, Firstname, Lastname, Username) are first scraped from the
?page=userpage. This step is crucial to prevent data corruption. - Shell Upload: A
multipart/form-datarequest is sent toclasses/Users.php?f=save.- The scraped user details are added as form data.
- A randomly named file containing PHP code and having a
.phpextension is added to theimgfield. - Payload:
<?php if(isset($_GET['cmd'])){ echo '<pre>'; $cmd = ($_GET['cmd']); system($cmd); echo '</pre>'; die; } ?>
- Execution: After the upload, the profile page is checked again to find the path (
srcattribute) of the uploaded file. Finally, a command execution test is performed by appending?cmd=whoamito this path.
Remediation
To fix these vulnerabilities, the following steps should be taken:
1. Prevent SQL Injection:
Use Prepared Statements instead of string concatenation in database queries.
Secure Example:
$stmt = $this->conn->prepare("SELECT * from users where username = ? and password = md5(?)");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
2. Secure File Upload:
File extensions and types must be strictly validated. Filenames should not be taken from the user but should be randomly generated by the server.
Secure Example:
$allowed = array('jpg', 'jpeg', 'png', 'gif');
$ext = pathinfo($_FILES['img']['name'], PATHINFO_EXTENSION);
if(!in_array(strtolower($ext), $allowed)) {
exit("Invalid file type");
}
$fname = 'uploads/' . uniqid() . '.' . $ext;
CVE ID: CVE-2025-69460
Original public disclosure: August 17, 2021
Researcher: Tağmaç "Tagoletta"
Canonical reference: https://www.exploit-db.com/exploits/50214
Github Repository: https://github.com/Tagoletta/CVE-2025-69460
Giriş
Bu raporda, "Simple Image Gallery 1.0" 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ı olarak amacım, kimlik doğrulaması gerektirmeyen bir saldırı vektörü üzerinden sunucuda tam kontrol sağlamaktı. Saldırı zinciri iki temel zafiyetten oluşmaktadır: 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, uygulamanın giriş mekanizmasıydı. classes/Login.php dosyasını incelediğimde, kullanıcı girişlerinin veritabanı sorgusuna doğrudan dahil edildiğini fark ettim.
// classes/Login.php içindeki Zafiyetli Kod
$qry = $this->conn->query("SELECT * from users where username = '$username' and password = md5('$password') ");
Görüldüğü üzere, $username değişkeni herhangi bir temizleme işleminden geçirilmeden SQL sorgusuna eklenmektedir. Bu durum, saldırganın sorgu mantığını manipüle etmesine olanak tanır.
Kullanılan Yük (Payload): admin' or '1'='1'#
Bu yük kullanıldığında sorgu şu hale dönüşür:
SELECT * from users where username = 'admin' or '1'='1'#' and password = md5('...')
# karakteri sorgunun geri kalanını (şifre kontrolünü) devre dışı bırakır ve 1=1 koşulu her zaman doğru olduğu için veritabanı ilk kullanıcıyı (genellikle yönetici) döndürür. Böylece şifreye ihtiyaç duymadan sisteme giriş yapılmış olur.
Aşama 2: Uzaktan Kod Yürütme (Kısıtlanmamış Dosya Yükleme)
Yönetici erişimi sağladıktan sonra, dosya yükleme fonksiyonlarını inceledim. Kullanıcı profilini güncelleme işlemi sırasında (classes/Users.php), yüklenen dosyaların yeterince kontrol edilmediğini tespit ettim.
// 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 güvenlik açıkları şunlardır:
- Uzantı Kontrolü Yok: Kod, dosya adını (
$_FILES['img']['name']) doğrudan kullanır ve uzantının güvenli olup olmadığını (örneğin sadece.jpgveya.png) kontrol etmez. - İçerik Kontrolü Yok: Dosyanın içeriği veya MIME türü doğrulanmaz.
- Çalıştırılabilir Dosya:
.phpuzantılı bir dosya yüklendiğinde, sunucu bunu bir PHP betiği olarak kabul eder ve çalıştırır.
Bu zafiyet, saldırganın sunucuya kötü amaçlı bir PHP dosyası (web shell) yüklemesine ve bu dosyayı tarayıcı üzerinden çağırarak komut çalıştırmasına olanak tanır.
Exploit Geliştirme
Bu saldırı zincirini otomatize etmek için bir Python betiği (exploit.py) geliştirdim. Betiğin çalışma mantığı şu şekildedir:
- Oturum Başlatma:
requests.session()kullanılarak HTTP oturumu başlatılır. - Giriş Atlatma:
classes/Login.php?f=loginadresine SQL enjeksiyon yükü (admin' or '1'='1'#) gönderilir. Başarılı yanıt alındığında oturum açılmış olur. - Kullanıcı Bilgilerini Koruma: Profil güncelleme işlemi tüm alanları değiştirdiği için, önce mevcut kullanıcı bilgileri (ID, Ad, Soyad, Kullanıcı Adı)
?page=usersayfasından çekilir (scrape edilir). Bu adım, mevcut verilerin bozulmasını önlemek için önemlidir. - Shell Yükleme:
classes/Users.php?f=saveadresinemultipart/form-dataisteği gönderilir.- Çekilen kullanıcı bilgileri form verisi olarak eklenir.
imgalanına, içinde PHP kodu bulunan ve.phpuzantılı rastgele isimlendirilmiş bir dosya eklenir.- Payload:
<?php if(isset($_GET['cmd'])){ echo '<pre>'; $cmd = ($_GET['cmd']); system($cmd); echo '</pre>'; die; } ?>
- Çalıştırma: Yükleme işleminden sonra profil sayfası tekrar kontrol edilerek yüklenen dosyanın yolu (
srcözniteliği) bulunur. Son olarak, bu yola?cmd=whoamiparametresi eklenerek komut çalıştırma testi yapılır.
Çözüm ve Kapatma
Bu zafiyetlerin giderilmesi için aşağıdaki adımlar uygulanmalıdır:
1. SQL Enjeksiyonunu Önleme:
Veritabanı sorgularında dize birleştirme yerine Hazırlanmış İfadeler (Prepared Statements) kullanılmalıdır.
Güvenli Örnek:
$stmt = $this->conn->prepare("SELECT * from users where username = ? and password = md5(?)");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
2. Dosya Yükleme Güvenliği:
Yüklenen dosyaların uzantıları ve türleri sıkı bir şekilde kontrol edilmelidir. Dosya isimleri kullanıcıdan alınmamalı, sunucu tarafından rastgele oluşturulmalıdır.
Güvenli Örnek:
$allowed = array('jpg', 'jpeg', 'png', 'gif');
$ext = pathinfo($_FILES['img']['name'], PATHINFO_EXTENSION);
if(!in_array(strtolower($ext), $allowed)) {
exit("Invalid file type");
}
$fname = 'uploads/' . uniqid() . '.' . $ext;
CVE ID: CVE-2025-69460
İlk Yayın Tarihi: 17 Ağustos 2021
Araştırmacı: Tağmaç "Tagoletta"
Canonical reference: https://www.exploit-db.com/exploits/50214
Github Repository: https://github.com/Tagoletta/CVE-2025-69460
Frequently Asked Questions
What is CVE-2025-69460?
An unauthenticated Remote Code Execution vulnerability in Simple Image Gallery 1.0, discovered as a zero-day by Tağmaç 'Tagoletta'.
What is the root cause of CVE-2025-69460?
An unrestricted file upload that lets an attacker upload an executable script such as PHP, which the server then runs, granting command execution without authentication.
How can an attacker exploit it?
By uploading a crafted image/script file that bypasses the type checks and then requesting its URL, triggering server-side code execution.
How do you remediate CVE-2025-69460?
Validate file type by content, store uploads outside the web root, rename files and strip execution permissions, and disable script execution in the upload directory.
Where is the PoC exploit writeup for CVE-2025-69460?
The full proof-of-concept exploit code and step-by-step writeup are published on this site — see the companion exploit page for the ready-to-run PoC that uploads a webshell via the unrestricted file upload for RCE.