![p0qbke2.png](https://i.hizliresim.com/p0qbke2.png)
~
Herkese selamlar, ben Suppressor. Bugün sizlere bilinmeyen ama basit bir açıktan bahsedeceğim. Zafiyetin adı "OS Command Injection" yani komut enjeksiyonu. İlk olarak zafiyeti tanıyalım. Zafiyetin isminden de anlaşılacağı gibi, bir işletim sistemi komutlarını çalıştırmamıza olanak sağlayan bir açıktır. Örneğin Linux'ta (cd, ls) veya Windows'ta (cls, dir) cmd ile ping atmayı deneyelim.
![475rn3p.png](https://i.hizliresim.com/475rn3p.png)
Evet, isteğimiz başarılı. Peki, bunu Flask ile bir web sitesi üzerinden yapacak olsak?
İlk olarak gerekli dosya ve klasörleri oluşturalım:
- app.py
- templates/index.html
app.py
Python:
from flask import Flask, request, render_template
import os
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
@app.route('/ping', methods=['POST'])
def ping():
ip = request.form['ip']
command = f"ping {ip}"
result = os.popen(command).read()
return render_template('index.html', response=result)
if __name__ == '__main__':
app.run(debug=True)
HTML
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
background-color: black;
text-align: center;
}
p{
color: white;
}
</style>
</head>
<body>
<form action="/ping" method="post">
<p>IP Address:</p> <input type="text" name="ip">
<input type="submit" value="Ping">
</form>
<br>
<p>{{ response }}</p>
</body>
</html>
![ctf0sk9.png](https://i.hizliresim.com/ctf0sk9.png)
![umBqNLP.png](https://i.imgur.com/umBqNLP.png)
Kodların açıklanması ve zafiyetin kaynağı:
Önemli bir not: Bu açık PHP'de daha mümkündür ve daha basit bir şekilde gerçekleştirilebilir. Bunun için Python'u biraz zorladım. Örneğin, subprocessi kullanırsanız herhangi bir sorunla karşılaşmazsınız. Ayrıca Python ile böyle bir zafiyetin oluşması oldukça zordur.
Kodları web üzerinden göstermek için Flask, form verilerini almak için requests, bir index göstermek için render_template, sistem komutu çalıştırmak için ise os ekliyoruz.
Python:
from flask import Flask, request, render_template
import os
İlk satırda flask nesnesini tanımlıyoruz arıdann route dekaratörü ile kök dizine bir yani ana url ye bir istek gittiği zaman index.html i return ediyoruz
Python:
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
Sistemde kodu çalıştırmak için bir eylem olması gerekmekte. Python Flask'te, JavaScript'teki onclick gibi fonksiyonlar olmadığı için /ping diye bir dizine POST request gelirse ilerliyoruz.
Python:
@app.route('/ping', methods=['POST'])
İlgili dizine POST request gelirse, requesti kullanarak form değerlerini çekeceğiz. İstek geldiğinde, IP ismini taşıyan form değerinin verisini alıp, ip isimli bir değişkene tanımlıyoruz.
Python:
def ping():
ip = request.form['ip']
Ardından command diye bir değişken tanımlayıp, ping attırma işlemini gerçekleştirmek için gerekli satırı tamamlıyoruz. Yani command değeri şu an ping Formdan_gelen_değer şeklinde. İlk olarak result diye bir değişken tanımlıyoruz ve ardından kodları yazıyoruz. os.popen fonksiyonu bize kısaca bir komutu çalıştırmamıza ve çıktısını almamıza yarar. read kısmı ise komutun çıktısını (response) bize string olarak döndürür. index.html'in tekrar kullanıcıya gösterilmesi ve verdiğimiz kodun çıktısını HTML'de görüntüleyebilmek için response diye bir parametre tanımlayıp, result değişkenini ona yazıyorum.
Python:
command = f"ping {ip}"
result = os.popen(command).read()
return render_template('index.html', response=result)
Kod bir kütüphane olarak eklenmeyip, direkt olarak çalıştırıldıysa kodu çalıştır :
Python:
if __name__ == '__main__':
app.run(debug=True)
![or14aef.png](https://i.hizliresim.com/or14aef.png)
Zafiyete biraz daha detaylı bakalım
Zafiyet ise tam olarak burada kaynaklanıyor. Herhangi bir karakter filtrelemesi yok. Kodumuz şu an şöyle çalışıyor:
Zafiyetli satır
Python:
result = os.popen(command).read()
![nc5o9kn.png](https://i.hizliresim.com/nc5o9kn.png)
Evet, böyle bir komut çalışıyor ama filtreleme yok. Ya birisi böyle bir şey yaparsa:
Rich (BB code):
ping 8.8.8.8 && dir
Artık "&&" operatörü kullanıldığı için, ilk komut tamamlandıktan sonra dir komutunu çalıştıracak ve zafiyet varsa makinedeki dosyalar görüntülenebilecek.
R&D = Research and development = Arge
![gan5qpm.png](https://i.hizliresim.com/gan5qpm.png)
R&D = Research and development = Arge
![🤔 🤔](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f914.png)
![ctf0sk9.png](https://i.hizliresim.com/ctf0sk9.png)
![umBqNLP.png](https://i.imgur.com/umBqNLP.png)
Zafiyeti filtrelemek
Burada regex/re denilen bir zımbırtı kullanıyoruz. Yakında ne işe yaradığını anlatacağım ama kısaca bu zımbırtı filtreleme işlemlerinde kullanılıyor. Güzel de bir kütüphanedir, evlenmek gibi bir niyetiniz varsa oldukça delikanlıdır. Kısaca işlevi, gelen input'un bir IP adresi olup olmadığını kontrol etmek.
Burada regex/re denilen bir zımbırtı kullanıyoruz. Yakında ne işe yaradığını anlatacağım ama kısaca bu zımbırtı filtreleme işlemlerinde kullanılıyor. Güzel de bir kütüphanedir, evlenmek gibi bir niyetiniz varsa oldukça delikanlıdır. Kısaca işlevi, gelen input'un bir IP adresi olup olmadığını kontrol etmek.
Python:
from flask import Flask, request, render_template
import subprocess
import re
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
@app.route('/ping', methods=['POST'])
def ping():
ip = request.form['ip']
if not re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", ip):
return render_template('index.html', response="Geçersiz IP adresi")
try:
result = subprocess.check_output(['ping', ip], universal_newlines=True)
except subprocess.CalledProcessError as e:
result = f"Hata: {e}"
return render_template('index.html', response=result)
if __name__ == '__main__':
app.run(debug=True)
![ctf0sk9.png](https://i.hizliresim.com/ctf0sk9.png)
![umBqNLP.png](https://i.imgur.com/umBqNLP.png)
Son düzenleme: