Improve security by preventing replay attacks

This commit is contained in:
Wizou 2022-05-19 01:32:22 +02:00
parent 3be28f0fbd
commit a8d2dfcfa1
4 changed files with 53 additions and 11 deletions

4
FAQ.md
View file

@ -94,7 +94,7 @@ If you use the Github source project in an old .NET Framework 4.x or .NET Core x
To fix this, you should also switch to using the [WTelegramClient Nuget package](https://www.nuget.org/packages/WTelegramClient) as it will install the required dependencies for it to work.
<a name="abuse"></a>
#### 7. I get errors FLOOD_WAIT_X or PEER_FLOOD, PHONE_NUMBER_BANNED. I can't import phone numbers.
#### 7. I get errors FLOOD_WAIT_X or PEER_FLOOD, PHONE_NUMBER_BANNED, USER_DEACTIVATED_BAN. I can't import phone numbers.
You can get these kind of problems if you abuse Telegram [Terms of Service](https://telegram.org/tos), or the [API Terms of Service](https://core.telegram.org/api/terms), or make excessive requests.
@ -108,7 +108,7 @@ If you think your phone number was banned from Telegram for a wrong reason, you
In any case, WTelegramClient is not responsible for the bad usage of the library and we are not affiliated to Telegram teams, so there is nothing we can do.
<a name="prevent-ban"></a>
#### 8. How to not get banned from Telegram?
#### 8. How to NOT get banned from Telegram?
**Do not share publicly your app's ID and hash!** They cannot be regenerated and are bound to your Telegram account.

View file

@ -383,12 +383,27 @@ namespace WTelegram
byte[] decrypted_data = EncryptDecryptMessage(data.AsSpan(24, (dataLen - 24) & ~0xF), false, _dcSession.AuthKey, data, 8, _sha256Recv);
if (decrypted_data.Length < 36) // header below+ctorNb
throw new ApplicationException($"Decrypted packet too small: {decrypted_data.Length}");
_sha256Recv.TransformBlock(_dcSession.AuthKey, 96, 32, null, 0);
_sha256Recv.TransformFinalBlock(decrypted_data, 0, decrypted_data.Length);
if (!data.AsSpan(8, 16).SequenceEqual(_sha256Recv.Hash.AsSpan(8, 16)))
throw new ApplicationException($"Mismatch between MsgKey & decrypted SHA256");
_sha256Recv.Initialize();
using var reader = new TL.BinaryReader(new MemoryStream(decrypted_data), this);
var serverSalt = reader.ReadInt64(); // int64 salt
var sessionId = reader.ReadInt64(); // int64 session_id
var msgId = reader.ReadInt64(); // int64 message_id
var seqno = reader.ReadInt32(); // int32 msg_seqno
var length = reader.ReadInt32(); // int32 message_data_length
if (length < 0 || length % 4 != 0) throw new ApplicationException($"Invalid message_data_length: {length}");
if (decrypted_data.Length - 32 - length is < 12 or > 1024) throw new ApplicationException($"Invalid message padding length: {decrypted_data.Length - 32}-{length}");
if (sessionId != _dcSession.Id) throw new ApplicationException($"Unexpected session ID: {sessionId} != {_dcSession.Id}");
if ((msgId & 1) == 0) throw new ApplicationException($"msg_id is not odd: {msgId}");
if (!_dcSession.CheckNewMsgId(msgId))
{
Helpers.Log(3, $"{_dcSession.DcID}>Ignoring duplicate or old msg_id {msgId}");
return null;
}
if (_lastRecvMsgId == 0) // resync ServerTicksOffset on first message
_dcSession.ServerTicksOffset = (msgId >> 32) * 10000000 - DateTime.UtcNow.Ticks + 621355968000000000L;
var msgStamp = MsgIdToStamp(_lastRecvMsgId = msgId);
@ -401,15 +416,7 @@ namespace WTelegram
if (_saltChangeCounter >= 30)
throw new ApplicationException($"Server salt changed too often! Security issue?");
}
if (sessionId != _dcSession.Id) throw new ApplicationException($"Unexpected session ID {sessionId} != {_dcSession.Id}");
if ((msgId & 1) == 0) throw new ApplicationException($"Invalid server msgId {msgId}");
if ((seqno & 1) != 0) lock (_msgsToAck) _msgsToAck.Add(msgId);
if (decrypted_data.Length - 32 - length is < 12 or > 1024) throw new ApplicationException($"Unexpected decrypted message_data_length {length} / {decrypted_data.Length - 32}");
_sha256Recv.TransformBlock(_dcSession.AuthKey, 96, 32, null, 0);
_sha256Recv.TransformFinalBlock(decrypted_data, 0, decrypted_data.Length);
if (!data.AsSpan(8, 16).SequenceEqual(_sha256Recv.Hash.AsSpan(8, 16)))
throw new ApplicationException($"Mismatch between MsgKey & decrypted SHA256");
_sha256Recv.Initialize();
var ctorNb = reader.ReadUInt32();
if (ctorNb != Layer.BadMsgCtor && (msgStamp - DateTime.UtcNow).Ticks / TimeSpan.TicksPerSecond is > 30 or < -300)

View file

@ -276,7 +276,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
private static byte[] AES_IGE_EncryptDecrypt(Span<byte> input, byte[] aes_key, byte[] aes_iv, bool encrypt)
{
if (input.Length % 16 != 0) throw new ApplicationException("intput size not divisible by 16");
if (input.Length % 16 != 0) throw new ApplicationException("AES_IGE input size not divisible by 16");
// code adapted from PHP implementation found at https://mgp25.com/AESIGE/
var output = new byte[input.Length];

View file

@ -33,6 +33,41 @@ namespace WTelegram
internal int DcID => DataCenter?.id ?? 0;
internal IPEndPoint EndPoint => DataCenter == null ? null : new(IPAddress.Parse(DataCenter.ip_address), DataCenter.port);
internal void Renew() { Helpers.Log(3, $"Renewing session on DC {DcID}..."); Id = Helpers.RandomLong(); Seqno = 0; LastSentMsgId = 0; }
const int msgIdsN = 512;
private long[] msgIds;
private int msgIdsHead;
internal bool CheckNewMsgId(long msg_id)
{
if (msgIds == null)
{
msgIds = new long[msgIdsN];
for (int i = 0; i < msgIdsN; i++) msgIds[i] = msg_id;
return true;
}
int newHead = (msgIdsHead + 1) % msgIdsN;
if (msg_id > msgIds[msgIdsHead])
msgIds[msgIdsHead = newHead] = msg_id;
else if (msg_id <= msgIds[newHead])
return false;
else
{
int min = 0, max = msgIdsN - 1;
while (min <= max) // binary search (rotated at newHead)
{
int mid = (min + max) / 2;
int sign = msg_id.CompareTo(msgIds[(mid + newHead) % msgIdsN]);
if (sign == 0) return false;
else if (sign < 0) max = mid - 1;
else min = mid + 1;
}
msgIdsHead = newHead;
for (min = (min + newHead) % msgIdsN; newHead != min;)
msgIds[newHead] = msgIds[newHead = newHead == 0 ? msgIdsN - 1 : newHead - 1];
msgIds[min] = msg_id;
}
return true;
}
}
public DateTime SessionStart => _sessionStart;