2023 年 12 月 16 日から開催されていた Backdoor CTF 2023 に 0nePadding で参加していました。
我参加了 Backdoor CTF 2023,该 CTF 于 2023 年 12 月 16 日举行,使用 0nePadding。
今回は参加メンバー少な目でしたが 57 位 / 779 チームでした。
这一次,参赛者只有少数,但有779支队伍排在第57位。
いくつか興味深い問題があり楽しかったです。 有一些有趣的问题,这很有趣。
いつも通り Writeup を書いていきます。 我将像往常一样写 Writeup。
もくじ 莫库吉
- Beginner-menace(Forensic)
初学者威胁(法医) - Headache(Forensic) 头痛(法医)
- Indecipherable-image-or-is-it?(Forensic)
难以辨认的图像还是它?(法医) - OpenSesame(Rev) 开盘芝麻(Rev)
- Secret Door(Rev) 秘密之门(Rev)
Beginner-menace(Forensic)
初学者威胁(法医)
Go off now and enjoy the 2 days of unlimited fun.
现在就出发,享受两天的无限乐趣。
問題バイナリとして与えられたファイルの Exif を参照すると Flag を取得できます。
您可以通过引用作为问题二进制文件的 Exif 来获取 Flag。
$ exiftool friend.jpeg
ExifTool Version Number : 12.40
File Name : friend.jpeg
Directory : .
File Size : 6.3 KiB
File Modification Date/Time : 2023:12:16 20:26:01+00:00
File Access Date/Time : 2023:12:16 20:26:01+00:00
File Inode Change Date/Time : 2023:12:16 20:26:01+00:00
File Permissions : -rw-r--r--
File Type : JPEG
File Type Extension : jpg
MIME Type : image/jpeg
JFIF Version : 1.01
Exif Byte Order : Big-endian (Motorola, MM)
X Resolution : 1
Y Resolution : 1
Resolution Unit : None
Artist : flag{7h3_r34l_ctf_15_7h3_fr13nd5_w3_m4k3_al0ng}
Y Cb Cr Positioning : Centered
Image Width : 266
Image Height : 190
Encoding Process : Baseline DCT, Huffman coding
Bits Per Sample : 8
Color Components : 3
Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2)
Image Size : 266x190
Megapixels : 0.051
Headache(Forensic) 头痛(法医)
I’ve had a headache since last evening. Is there a magic spell that can cure it immediately?
从昨晚开始,我就头疼了。有没有可以立即治愈的魔法咒语?
問題バイナリとして与えられたファイルは破損していたためバイナリエディタで開いてみたところ、IHDR チャンクと思しき領域はあるものの、先頭のマジックナンバーが png ファイルのものではありませんでした。
作为问题二进制文件给出的文件已损坏,因此当我在二进制编辑器中打开它时,我发现有一个区域似乎是 IHDR 块,但前导幻数不是 png 文件。
問題名が Headache ということもあり、ヘッダが破壊されていることが問題のようです。
由于问题名称是 Headache,因此问题似乎是标头被破坏了。
そこで、先頭の 4 バイトを 89 50 4E 47
に変更しました。
所以我把前 4 个字节 89 50 4E 47
改成了 .
すると、ファイルが修復され、以下の画像を開くことができました。
然后,文件被修复,我能够打开下面的图像。
しかし、flag{8p3ll_15_8g_50_4E_47}
など様々なパターンを試しても正しい Flag として通すことができませんでした。
但是,即使尝试了各种 flag{8p3ll_15_8g_50_4E_47}
模式,我也无法让它作为正确的标志传递。
そこで、チームメンバーの鋭いコメントを元に flag{sp3ll_15_89_50_4E_47}
を入力したところ、Flag を通すことができました。
因此,根据团队成员的尖锐评论,我 flag{sp3ll_15_89_50_4E_47}
输入并能够传递旗帜。
8 を s に置き換えさせるのは guess 味が強いのではと思いましたが解けたので OK です。
我以为用 s 替换 8 会是一个强烈的猜测,但现在它已经解决了,没关系。
Indecipherable-image-or-is-it?(Forensic)
难以辨认的图像还是它?(法医)
How ‘bout an easy Steg challenge, Just remember that brute-forcing is not an option!
多么容易的 Steg 挑战,请记住,暴力破解不是一种选择!
問題バイナリとして与えられた png ファイルに対して zsteg -a
コマンドでスキャンを実行すると zip 化された jpeg ファイルのデータと keka{1b0asx2w_hbin9K_Ah_6xwm0L}
という Flag っぽい文字列が埋まっていることがわかります。
问题:如果您扫描以二进制形式给出的 png 文件,您将看到压缩的 jpeg 文件 zsteg -a
数据和 keka{1b0asx2w_hbin9K_Ah_6xwm0L}
类似 Flag 的字符串被填充。
この ZIP ファイルを抽出して展開した後、stegbrute を使用して steghide のブルートフォースを実施したところ、F4K5FL4G
という文字列が書き込まれたファイルを取得することができました。
提取并提取ZIP文件后,我使用stegbrute暴力破解隐写,并能够检索带有写入字符串 F4K5FL4G
的文件。
コンテスト中はここで手詰まりになってしまったものの、どうやら F4K5FL4G
をキーとして文字列 keka{1b0asx2w_hbin9K_Ah_6xwm0L}
を Vigenere 暗号で復号すると正しい Flag が得られたようです。
在比赛中,我们被困在这里,但显然我们通过使用 Vigenere 密码作为 F4K5FL4G
密钥来解密字符串 keka{1b0asx2w_hbin9K_Ah_6xwm0L}
,从而获得了正确的标志。
括弧やアンダーバーが維持されていそうだったので換字式暗号や ROT の類だというあたりはつけていたのですが、単純に Cyberchef にかけるだけだと復号ができず、Vigenere 暗号での復号を試行錯誤するまでには至っておりませんでした。
由于括号和下划线似乎被保留了下来,我补充说它是一种替代密码或一种 ROT,但我无法通过简单地调用 Cyberchef 来解密它,而且我还没有达到使用 Vigenere 加密进行解密的试错地步。
最終的に dcode.fr にて Alphabet を ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
にしてかつ Key を F4K5FL4G
とした場合に正しい Flag flag{v1g5n5r3_c1ph4R_1n_1m4g5S}
を取得できることがわかりました。
最后,发现可以通过将字母 ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
设置为和键 F4K5FL4G
设置为 dcode.fr 来获得正确的标志 flag{v1g5n5r3_c1ph4R_1n_1m4g5S}
。
Alphabet の配置はちょっと guess が強すぎる気がしますね。
我觉得 Alphabet 的位置有点太猜测了。
OpenSesame(Rev) 开盘芝麻(Rev)
Whisper the phrase, unveil with ‘Open Sesame
低声说出这句话,用“芝麻开门”揭开面纱
問題バイナリとして与えられた apk を展開して解析してみたところユーザ名とパスワードの検証を行うアプリであり、ネイティブライブラリは使用していないことを確認しました。
当我提取并分析作为问题二进制文件的 apk 时,我确认它是一个验证用户名和密码的应用程序,并且不使用本机库。
そのため、まずは MainActivity.smali を
因此,首先,将 MainActivity.smali 添加到
public class com.example.open_sesame.MainActivity extends androidx.appcompat.app.AppCompatActivity {
/* .source "MainActivity.java" */
/* # static fields */
private static final valid_password;
private static final java.lang.String valid_user;
/* # instance fields */
private android.widget.Button buttonLogin;
private android.widget.EditText editTextPassword;
private android.widget.EditText editTextUsername;
/* # direct methods */
static com.example.open_sesame.MainActivity ( ) {
/* .locals 1 */
int v0 = 7; // const/4 v0, 0x7
/* new-array v0, v0, [I */
/* fill-array-data v0, :array_0 */
return;
/* nop */
/* :array_0 */
/* .array-data 4 */
/* 0x34 */
/* 0x6c */
/* 0x31 */
/* 0x62 */
/* 0x61 */
/* 0x62 */
/* 0x61 */
} // .end array-data
} // .end method
public com.example.open_sesame.MainActivity ( ) {
/* .locals 0 */
/* .line 11 */
/* invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V */
return;
} // .end method
static void access$000 ( com.example.open_sesame.MainActivity p0 ) { //synthethic
/* .locals 0 */
/* .line 11 */
/* invoke-direct {p0}, Lcom/example/open_sesame/MainActivity;->validateCredentials()V */
return;
} // .end method
private java.lang.String flag ( java.lang.String p0, java.lang.String p1 ) {
/* .locals 4 */
/* .line 91 */
/* new-instance v0, Ljava/lang/StringBuilder; */
/* invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V */
int v1 = 0; // const/4 v1, 0x0
/* .line 92 */
} // :goto_0
v2 = (( java.lang.String ) p2 ).length ( ); // invoke-virtual {p2}, Ljava/lang/String;->length()I
/* if-ge v1, v2, :cond_0 */
/* .line 93 */
v2 = (( java.lang.String ) p2 ).charAt ( v1 ); // invoke-virtual {p2, v1}, Ljava/lang/String;->charAt(I)C
v3 = (( java.lang.String ) p1 ).length ( ); // invoke-virtual {p1}, Ljava/lang/String;->length()I
/* rem-int v3, v1, v3 */
v3 = (( java.lang.String ) p1 ).charAt ( v3 ); // invoke-virtual {p1, v3}, Ljava/lang/String;->charAt(I)C
/* xor-int/2addr v2, v3 */
/* int-to-char v2, v2 */
/* .line 94 */
(( java.lang.StringBuilder ) v0 ).append ( v2 ); // invoke-virtual {v0, v2}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder;
/* add-int/lit8 v1, v1, 0x1 */
/* .line 96 */
} // :cond_0
(( java.lang.StringBuilder ) v0 ).toString ( ); // invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
} // .end method
private it4chi ( java.lang.String p0 ) {
/* .locals 3 */
/* .line 68 */
v0 = (( java.lang.String ) p1 ).length ( ); // invoke-virtual {p1}, Ljava/lang/String;->length()I
/* new-array v0, v0, [I */
int
v1 = 0; // const/4 v1, 0x0
/* .line 69 */
} // :goto_0
v2 = (( java.lang.String ) p1 ).length ( ); // invoke-virtual {p1}, Ljava/lang/String;->length()I
/* if-ge v1, v2, :cond_0 */
/* .line 70 */
v2 = (( java.lang.String ) p1 ).charAt ( v1 ); // invoke-virtual {p1, v1}, Ljava/lang/String;->charAt(I)C
/* aput v2, v0, v1 */
/* add-int/lit8 v1, v1, 0x1 */
} // :cond_0
} // .end method
private Boolean n4ut1lus ( java.lang.String p0 ) {
/* .locals 4 */
/* .line 54 */
/* invoke-direct {p0, p1}, Lcom/example/open_sesame/MainActivity;->it4chi(Ljava/lang/String;)[I */
/* .line 55 */
/* array-length v0, p1 */
v1 = com.example.open_sesame.MainActivity.valid_password;
/* array-length v1, v1 */
int v2 = 0; // const/4 v2, 0x0
/* if-eq v0, v1, :cond_0 */
} // :cond_0
/* move v0, v2 */
/* .line 59 */
} // :goto_0
/* array-length v1, p1 */
/* if-ge v0, v1, :cond_2 */
/* .line 60 */
/* aget v1, p1, v0 */
v3 = com.example.open_sesame.MainActivity.valid_password;
/* aget v3, v3, v0 */
/* if-eq v1, v3, :cond_1 */
} // :cond_1
/* add-int/lit8 v0, v0, 0x1 */
} // :cond_2
int p1 = 1; // const/4 p1, 0x1
} // .end method
private java.lang.String sh4dy ( java.lang.String p0 ) {
/* .locals 4 */
/* .line 76 */
/* new-instance v0, Ljava/lang/StringBuilder; */
/* invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V */
int v1 = 0; // const/4 v1, 0x0
/* .line 78 */
} // :goto_0
v2 = (( java.lang.String ) p1 ).length ( ); // invoke-virtual {p1}, Ljava/lang/String;->length()I
/* if-ge v1, v2, :cond_1 */
/* .line 79 */
v2 = (( java.lang.String ) p1 ).charAt ( v1 ); // invoke-virtual {p1, v1}, Ljava/lang/String;->charAt(I)C
/* .line 80 */
v3 = java.lang.Character .isDigit ( v2 );
if ( v3 != null) { // if-eqz v3, :cond_0
/* .line 81 */
(( java.lang.StringBuilder ) v0 ).append ( v2 ); // invoke-virtual {v0, v2}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder;
} // :cond_0
/* add-int/lit8 v1, v1, 0x1 */
/* .line 84 */
} // :cond_1
(( java.lang.StringBuilder ) v0 ).toString ( ); // invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
} // .end method
private void showToast ( java.lang.String p0 ) {
/* .locals 1 */
int v0 = 0; // const/4 v0, 0x0
/* .line 100 */
android.widget.Toast .makeText ( p0,p1,v0 );
(( android.widget.Toast ) p1 ).show ( ); // invoke-virtual {p1}, Landroid/widget/Toast;->show()V
return;
} // .end method
private Integer sl4y3r ( java.lang.String p0 ) {
/* .locals 0 */
/* .line 87 */
p1 = java.lang.Integer .parseInt ( p1 );
/* add-int/lit8 p1, p1, -0x1 */
} // .end method
private void validateCredentials ( ) {
/* .locals 3 */
v0 = this.editTextUsername;
/* .line 38 */
(( android.widget.EditText ) v0 ).getText ( ); // invoke-virtual {v0}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
(( java.lang.Object ) v0 ).toString ( ); // invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String;
(( java.lang.String ) v0 ).trim ( ); // invoke-virtual {v0}, Ljava/lang/String;->trim()Ljava/lang/String;
v1 = this.editTextPassword;
/* .line 39 */
(( android.widget.EditText ) v1 ).getText ( ); // invoke-virtual {v1}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
(( java.lang.Object ) v1 ).toString ( ); // invoke-virtual {v1}, Ljava/lang/Object;->toString()Ljava/lang/String;
(( java.lang.String ) v1 ).trim ( ); // invoke-virtual {v1}, Ljava/lang/String;->trim()Ljava/lang/String;
final String v2 = "Jack Ma"; // const-string v2, "Jack Ma"
/* .line 41 */
v0 = (( java.lang.String ) v0 ).equals ( v2 ); // invoke-virtual {v0, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
if ( v0 != null) { // if-eqz v0, :cond_0
v0 = /* invoke-direct {p0, v1}, Lcom/example/open_sesame/MainActivity;->n4ut1lus(Ljava/lang/String;)Z */
}
if ( v0 != null) { // if-eqz v0, :cond_0
/* .line 42 */
// 4l1baba から数字の抽出?
/* invoke-direct {p0, v1}, Lcom/example/open_sesame/MainActivity;->sh4dy(Ljava/lang/String;)Ljava/lang/String; */
/* .line 43 */
v0 = /* invoke-direct {p0, v0}, Lcom/example/open_sesame/MainActivity;->sl4y3r(Ljava/lang/String;)I */
/* .line 45 */
/* new-instance v1, Ljava/lang/StringBuilder; */
final String v2 = "flag{"; // const-string v2, "flag{"
/* invoke-direct {v1, v2}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V */
java.lang.Integer .toString ( v0 );
final String v2 = "U|]rURuoU^PoR_FDMo@X]uBUg"; // const-string v2, "U|]rURuoU^PoR_FDMo@X]uBUg"
/* invoke-direct {p0, v0, v2}, Lcom/example/open_sesame/MainActivity;->flag(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; */
(( java.lang.StringBuilder ) v1 ).append ( v0 ); // invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
final String v1 = "}"; // const-string v1, "}"
(( java.lang.StringBuilder ) v0 ).append ( v1 ); // invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
(( java.lang.StringBuilder ) v0 ).toString ( ); // invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
} // :cond_0
final String v0 = "Invalid credentials.Please try again."; // const-string v0, "Invalid credentials.Please try again."
/* .line 49 */
/* invoke-direct {p0, v0}, Lcom/example/open_sesame/MainActivity;->showToast(Ljava/lang/String;)V */
} // :goto_0
return;
} // .end method
/* # virtual methods */
protected void onCreate ( android.os.Bundle p0 ) {
/* .locals 1 */
/* .line 22 */
/* invoke-super {p0, p1}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V */
/* .line 23 */
(( com.example.open_sesame.MainActivity ) p0 ).setContentView ( p1 ); // invoke-virtual {p0, p1}, Lcom/example/open_sesame/MainActivity;->setContentView(I)V
/* .line 25 */
(( com.example.open_sesame.MainActivity ) p0 ).findViewById ( p1 ); // invoke-virtual {p0, p1}, Lcom/example/open_sesame/MainActivity;->findViewById(I)Landroid/view/View;
/* check-cast p1, Landroid/widget/EditText; */
this.editTextUsername = p1;
/* .line 26 */
(( com.example.open_sesame.MainActivity ) p0 ).findViewById ( p1 ); // invoke-virtual {p0, p1}, Lcom/example/open_sesame/MainActivity;->findViewById(I)Landroid/view/View;
/* check-cast p1, Landroid/widget/EditText; */
this.editTextPassword = p1;
/* .line 27 */
(( com.example.open_sesame.MainActivity ) p0 ).findViewById ( p1 ); // invoke-virtual {p0, p1}, Lcom/example/open_sesame/MainActivity;->findViewById(I)Landroid/view/View;
/* check-cast p1, Landroid/widget/Button; */
this.buttonLogin = p1;
/* .line 29 */
/* new-instance v0, Lcom/example/open_sesame/MainActivity$1; */
/* invoke-direct {v0, p0}, Lcom/example/open_sesame/MainActivity$1;-><init>(Lcom/example/open_sesame/MainActivity;)V */
(( android.widget.Button ) p1 ).setOnClickListener ( v0 ); // invoke-virtual {p1, v0}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
return;
} // .end method
Secret Door(Rev) 秘密之门(Rev)
Sorry to have lost the key of the secret Gate like this… But believe me there is a way if you follow the Right Path..
很抱歉就这样丢失了秘密之门的钥匙……但相信我,如果你遵循正确的道路,就会有办法。
問題バイナリとして与えられた ELF ファイルを Ghidra で解析するとコマンドライン引数で 0x11 文字の文字列を受け取っているかの検証を行っています。
问题:当 Ghidra 解析作为二进制文件的 ELF 文件时,它会验证它是否接受一串 0x11 字符作为命令行参数。
local_20 = *(long *)(in_FS_OFFSET + 0x28);
if (param_1 != 2) {
std::operator<<((basic_ostream *)std::cout,"Just try to get the door");
/* WARNING: Subroutine does not return */
exit(0);
}
sVar2 = strlen(*(char **)(param_2 + 8));
if (sVar2 != 0x11) {
std::operator<<((basic_ostream *)std::cout,"that\'s not even a door :p");
/* WARNING: Subroutine does not return */
exit(0);
}
さらにこの検証を突破すると、スタックにハードコードされた文字列(SeventeenChars!!!
) を検証して一致する場合はこの文字列と
此外,如果违反此验证,则验证堆栈上的硬编码字符串 ( SeventeenChars!!!
),如果匹配,则将此字符串添加到
local_f4 = 0;
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string();
local_68 = 0x42;
local_64 = 0x77;
local_60 = 0x65;
local_5c = 0x71;
local_58 = 0x7b;
local_54 = 0x62;
local_50 = 0x72;
local_4c = 0x7d;
local_48 = 0x77;
local_44 = 0x59;
local_40 = 0x73;
local_3c = 0x7d;
local_38 = 0x6f;
local_34 = 0x6d;
local_30 = 0x3e;
local_2c = 1;
local_28 = 0;
for (; local_f4 < 0x11; local_f4 = local_f4 + 1) {
/* try { // try from 00102fa1 to 00102fa5 has its CatchHandler @ 00103183 */
/* } // end try from 00102fa1 to 00102fa5 */
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::push_back
((char)local_e8);
}
std::allocator<char>::allocator();
/* try { // try from 00102fe0 to 00102fe4 has its CatchHandler @ 00103126 */
/* } // end try from 00102fe0 to 00102fe4 */
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::
basic_string<std::allocator<char>>(local_c8,"ThatsHardcoded!!!",&local_f5);
std::allocator<char>::~allocator((allocator<char> *)&local_f5);
/* try { // try from 00103005 to 00103009 has its CatchHandler @ 0010316b */
/* } // end try from 00103005 to 00103009 */
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string
(local_88);
/* try { // try from 0010301e to 00103022 has its CatchHandler @ 00103156 */
/* } // end try from 0010301e to 00103022 */
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string
(local_a8);
/* try { // try from 00103034 to 00103038 has its CatchHandler @ 0010313e */
/* } // end try from 00103034 to 00103038 */
func_5(SUB81(local_a8,0),SUB81(local_88,0));
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string
((basic_string<char,std::char_traits<char>,std::allocator<char>> *)local_a8);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string
((basic_string<char,std::char_traits<char>,std::allocator<char>> *)local_88);
/* try { // try from 00103059 to 001030ed has its CatchHandler @ 0010316b */
local_f0 = (int *)operator.new[](0x44);
piVar3 = (int *)func_4(local_e8,*(char **)(param_2 + 8));
local_f0 = (int *)func_3(piVar3,(basic_string *)local_c8);
cVar1 = func_2(local_f0);
if (cVar1 == '\0') {
func_1(*local_f0,local_f0[0x10]);
}
else {
/* } // end try from 00103059 to 001030ed */
std::operator<<((basic_ostream *)std::cout,"Wrong door");
}
この func1 では、問題バイナリとして与えられた encoded.bin を読み取り、何かしらのキーで XOR 復号を行った後、ハードコードされたファイル名 thedoor.jpg として保存していることがわかります。
您可以看到 func1 读取作为问题二进制文件给出的编码.bin,使用某个键对其进行异或,然后将其保存为硬编码文件名 thedoor.jpg。
basic_string<std::allocator<char>>(local_448,"encoded.bin",local_468);
std::allocator<char>::~allocator((allocator<char> *)local_468);
/* try { // try from 0010276b to 0010276f has its CatchHandler @ 001029a5 */
/* } // end try from 0010276b to 0010276f */
std::basic_ifstream<char,std::char_traits<char>>::basic_ifstream
***
for (local_48c = 0; (int)local_48c < 0x16086; local_48c = local_48c + 1) {
if ((local_48c & 1) == 0) {
pbVar1 = (byte *)std::vector<char,std::allocator<char>>::operator[]
((vector<char,std::allocator<char>> *)local_468,
(long)(int)local_48c);
*pbVar1 = *pbVar1 ^ (byte)param_1;
}
else {
pbVar1 = (byte *)std::vector<char,std::allocator<char>>::operator[]
((vector<char,std::allocator<char>> *)local_468,
(long)(int)local_48c);
*pbVar1 = *pbVar1 ^ (byte)param_2;
}
}
動的解析でこのキーを特定してもよさそうでしたが、対象ファイルが jpg ということがわかったので、jpg のマジックナンバーで XOR を取ってキーを逆算することにしました。
我本可以通过动态分析来确定这个键,但是由于我发现目标文件是jpg,所以我决定用jpg幻数对键进行异运,然后向后计算键。
これによって N!
が正しいキーとなることがわかりました。
这证明这是 N!
正确的密钥。
このキーでファイルを復号することで以下の画像とともに Flag を取得することができました。
通过使用此密钥解密文件,我们能够获得 Flag 以及下图。
原文始发于kashiwaba-yuki:Backdoor CTF 2023 Writeup