Skip to content

Instantly share code, notes, and snippets.

@key-moon
Created October 8, 2018 02:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save key-moon/ccef0c2bae028aca8c9b10c0a7123d41 to your computer and use it in GitHub Desktop.
Save key-moon/ccef0c2bae028aca8c9b10c0a7123d41 to your computer and use it in GitHub Desktop.
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