-
-
Save key-moon/ccef0c2bae028aca8c9b10c0a7123d41 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Text; | |
using System.Threading.Tasks; | |
using CTFLibrary.TCP; | |
class Program | |
{ | |
static TCPStream stream = null; | |
static string flag = ""; | |
const int FLAG_BLOCK = 8; | |
static void Main() | |
{ | |
//flagに文字を追加するとそこから始められるようにした (フラグが途中まで出てプログラムが落ちた時用) | |
for (int i = flag.Length; true; i++) | |
{ | |
byte res; | |
while (true) | |
{ | |
Task<byte> task = null; | |
try | |
{ | |
//Byteを調べるタスクを作成、実行 | |
task = new Task<byte>(() => TryFindByte(i)); | |
task.Start(); | |
//タイムアウト処理(5秒で処理が終わらなかったらAbort | |
task.Wait(5000); | |
//完了していなかったら(タイムアウトだったら) | |
if (!task.IsCompleted) | |
{ | |
Debug.WriteLine("time out"); | |
continue; | |
} | |
//失敗の場合(失敗だと0を返す) | |
if (task.Result == 0) continue; | |
//成功していたらここまで来るので抜ける | |
res = task.Result; | |
break; | |
} | |
catch | |
{ | |
Debug.WriteLine($"Error : {task.Exception.Message}"); | |
continue; | |
} | |
} | |
//フラグの前にASCIIでresをエンコードしたのを付ける | |
flag = Encoding.ASCII.GetString(new byte[] { res }) + flag; | |
//現在のフラグを出力 | |
Console.WriteLine(flag); | |
} | |
} | |
//後ろからi番目のbyteに対して攻撃を実行する | |
//例外を投げるとハンドルが面倒くさいので、結果が出なかった場合は0x00を返している。(これはnullバイトを表すので、確実にフラグに含まれない) | |
static byte TryFindByte(int i) | |
{ | |
try | |
{ | |
//長く繋いでいると切られてしまうので、各イテレーション前にストリームを切る | |
ResetStream(); | |
//暗号文 | |
string[] cipherBlocks = GetFlagCipherBlocks(i); | |
//パディングブロック平文の最終ビット | |
byte padBlockPlaneLast = 0x10; | |
//解読したいブロック | |
string targetBlock = cipherBlocks[FLAG_BLOCK]; | |
//解読したいブロックにXORで作用するブロック(解読したいブロックの一つ前) | |
string targetOperateBlock = cipherBlocks[FLAG_BLOCK - 1]; | |
//パディングブロックに作用するブロック | |
string padOperateBlock = cipherBlocks[cipherBlocks.Length - 2]; | |
//解読したいブロックをpadding部分に挿入 | |
cipherBlocks[cipherBlocks.Length - 1] = targetBlock; | |
//現在検索しているバイト | |
byte searchedByte = (byte)(padBlockPlaneLast ^ ConvertFromHexString(padOperateBlock).Last() ^ ConvertFromHexString(targetOperateBlock).Last()); | |
if (VerifyMessage(cipherBlocks)) | |
{ | |
//デコードが成功した場合、デコード後の最終byteが0x10であるということ | |
Debug.WriteLine($"success targetBlock : {targetBlock} targetOperateBlock : {targetOperateBlock} padOperateBlock : {padOperateBlock}"); | |
return searchedByte; | |
} | |
else | |
{ | |
Debug.WriteLine("failed"); | |
return 0; | |
} | |
} | |
catch(Exception e) | |
{ | |
//Console.WriteLine($"Error : {e.Message}"); | |
return 0; | |
} | |
} | |
//フラグの後ろからi文字目がFLAG_BLOCK番目のブロックの先頭になっているような暗号文を取得 | |
static string[] GetFlagCipherBlocks(int i) | |
{ | |
try | |
{ | |
//endで挿入する文字数を増やして、Paddingをいい感じで増やしていく | |
string[] blocks = EncryptMessage(GetStrWithLength(17 + i - 2), GetStrWithLength(16 - ((i + 17) % 16))); | |
return blocks; | |
} | |
catch (Exception e) | |
{ | |
throw e; | |
} | |
} | |
//与えられるアプリケーションのWrapper関数(自前TCPライブラリマシマシなので読みにくい) | |
#region AppWrapper | |
//ストリームを再接続する | |
static void ResetStream() | |
{ | |
stream = new TCPStream("2018shell2.picoctf.com", 15596, Encoding.ASCII); | |
stream.ReadToEnd(); | |
} | |
//Encrypt messageを実行する関数 | |
static string[] EncryptMessage(string rep, string ps) | |
{ | |
try | |
{ | |
stream.ReadToEnd(); //Select an option: | |
stream.WriteLine("e"); //select "Encrypt" option | |
stream.ReadToEnd(); //Please enter your situation report: | |
stream.WriteLine(rep); //write report | |
stream.ReadToEnd(); //Anything else? | |
stream.WriteLine(ps); //write ps | |
//encrypted: xxxxxxのパース | |
string res = stream.ReadToEnd().Split()[1]; | |
string[] resArr = new string[res.Length / 32]; | |
for (int i = 0; i < resArr.Length; i++) resArr[i] = res.Substring(i * 32, 32); | |
return resArr; | |
} | |
catch | |
{ | |
throw new Exception(); | |
} | |
} | |
//VerifyMessage(string s) をstring配列でも実行可能にするWrapper関数 | |
static bool VerifyMessage(string[] s) | |
{ | |
try | |
{ | |
return VerifyMessage(string.Join("", s)); | |
} | |
catch (Exception e) | |
{ | |
throw e; | |
} | |
} | |
//Send & verifyを実行する関数 | |
static bool VerifyMessage(string s) | |
{ | |
try | |
{ | |
string s1 = stream.ReadToEnd(); | |
stream.WriteLine("s"); | |
string s2 = stream.ReadToEnd(); | |
//decode | |
stream.WriteLine(s); | |
string res = stream.ReadToEnd(); | |
return res.Contains("Successful"); | |
} | |
catch (Exception e) | |
{ | |
throw e; | |
} | |
} | |
#endregion | |
#region util | |
//特定の長さの文字列を返す() | |
static string GetStrWithLength(int len) => "".PadLeft(len, '_'); | |
//hexの文字列をbyteに区切って返す | |
static byte[] ConvertFromHexString(string s) | |
{ | |
List<byte> res = new List<byte>(); | |
for (int i = 0; i < s.Length; i += 2) | |
{ | |
res.Add(Convert.ToByte(s.Substring(i, 2), 16)); | |
} | |
return res.ToArray(); | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment