HUBUCTF-新生赛-fmt

文章发布时间:

最后更新时间:

文章总字数:
1.4k

预计阅读时间:
5 分钟

老规矩走个流程,checksec看一下保护机制

a

好像看不出什么,猜不出他想干啥,拖到ida里面瞧瞧

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
FILE *stream; // [rsp+8h] [rbp-68h]
char format[32]; // [rsp+10h] [rbp-60h] BYREF
char s[8]; // [rsp+30h] [rbp-40h] BYREF
__int64 v6; // [rsp+38h] [rbp-38h]
__int64 v7; // [rsp+40h] [rbp-30h]
__int64 v8; // [rsp+48h] [rbp-28h]
__int64 v9; // [rsp+50h] [rbp-20h]
__int64 v10; // [rsp+58h] [rbp-18h]
__int16 v11; // [rsp+60h] [rbp-10h]
unsigned __int64 v12; // [rsp+68h] [rbp-8h]

v12 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
stream = fopen("flag.txt", "r");
*(_QWORD *)s = 0LL;
v6 = 0LL;
v7 = 0LL;
v8 = 0LL;
v9 = 0LL;
v10 = 0LL;
v11 = 0;
if ( stream )
fgets(s, 50, stream);
HIBYTE(v11) = 0;
while ( 1 )
{
puts("Echo as a service");
gets(format);
printf(format);
putchar(10);
}
}

经典格式化字符串漏洞吧

把flag.txt的内容存储在变量s里面,这题得用到gdb了

教一个办法,像这种调用文件的,建议自己在本地建一个同名文件,方便查看文件在栈中的地址

b

可以看到我们自己在本机创建的flag.txt此时位于栈上的位置

AAAAAAAA就是我们在gets中输入的值

所以我们可以知道flag和格式化字符串的偏移是11(有可能会有差错,所以我的建议是等下泄露flag的时候扩大范围多试几个)

为什么是11?AAAAAAAA不是和flag只间隔了3个字长?这里是64位和32位栈传参的差异

64位和32位栈传参

我在初期栈学习中,就一直注重强调32位和64位的不同,因为我本人在初期学习中,就常常对这二者没有足够的分辨意识

接下来着重讲解这二者的不同

首先是我们之前已经详细讲过的32位传参

具体的传参方式就是在栈上传参,并且根据system和call system调用的不同,参数和函数地址的偏移也不同

我们在之前的阅读中,会注意到频繁出现的esp eip eax ebx等

这里的e就是32位特有,64位情况下的寄存器,通常是以r开头。例如rsp

64位传参的情况相较32位及其不同!!千万不要搞混

在linux操作系统中,前六个参数通过 RDI 、 RSI 、 RDX 、 RCX 、 R8 和 R9 传递

而在windows操作系统中,前四个参数通过 RCX 、 RDX 、 R8 和 R9 来传递

他们的共同点是,其第七个/第五个参数就push入栈进行传递(因此上面的偏移值才是6+5[5是从AAAAAAAA开始数到flag])

既然已经清楚了大致的偏移量,我们开始传入格式化字符串吧、

c

可以看到果然有些偏差,12才是正确的偏移量

这里又有一个问题了,为什么我们看到的是16进制形式的,而不是字符串形式

这就要从%x的用法着手分析了

x是打印出无0x的16进制

而我们换用%p试一下

d

转化成字符串看一下

1
7{FTCSSN

倒过来,是不是像一个flag的格式了,说明这题就是用%p

这是为什么?%p和%x有什么区别?而且这里为什么又是倒转过来的?别急,慢慢讲

1.为什么这里要用%p

%p是打印出所指栈位置中的地址指向的地方的内容

在搞懂这个问题前,我们得先知道,栈中是不会存储字符串的,这一点在栈溢出的时候就体现了出来

我们给system传参的时候是binsh字符串的地址,而不是binsh字符串

所以,看起来flag是存储到了栈中,其实只是他的地址被保存到了栈中

2.为什么是倒转过来的

这里涉及到了小端序和大端序的问题

这二者都属于字节序,什么是字节序?为什么要有字节序?

字节序指电脑内存中占用多个字节的数据的字节排列顺序

在几乎所有的平台上,多字节对象都被存储为连续的字节序列

为什么会有字节序,统一用大端序不行吗?答案是,计算机先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。在计算机内部,小端序被广泛应用于现代 CPU 内部存储数据;而在其他场景,比如网络传输和文件存储则使用大端序

那么什么是小端序和大端序?

大端序将数据的低位字节存放在内存的高位地址,高位字节存放在低位地址

小端序将一个多位数的低位放在较小的地址处,高位放在较大的地址处

看不懂没关系,图文演示一下

e

上图为小端序的存储状况,作为高位字节的12就放在了低地址

而大端序的存储,就比较符合我们人类的阅读习惯

这里因为大端序我们接触的少,再加上小端序已经作了详细的解释,同理可得,就不进行作图了(真的不是我懒)

截止到现在,本题涉及到的知识点已经全部讲完了,如果想练练手的话,可以试试ctfshow中的pwn04(格式化字符串泄露canary)