mirror of
https://github.com/wiz0u/WTelegramClient.git
synced 2025-12-06 06:52:01 +01:00
Improve security by preventing replay attacks
This commit is contained in:
parent
3be28f0fbd
commit
a8d2dfcfa1
4
FAQ.md
4
FAQ.md
|
|
@ -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.
|
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>
|
<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.
|
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.
|
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>
|
<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.
|
**Do not share publicly your app's ID and hash!** They cannot be regenerated and are bound to your Telegram account.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -383,12 +383,27 @@ namespace WTelegram
|
||||||
byte[] decrypted_data = EncryptDecryptMessage(data.AsSpan(24, (dataLen - 24) & ~0xF), false, _dcSession.AuthKey, data, 8, _sha256Recv);
|
byte[] decrypted_data = EncryptDecryptMessage(data.AsSpan(24, (dataLen - 24) & ~0xF), false, _dcSession.AuthKey, data, 8, _sha256Recv);
|
||||||
if (decrypted_data.Length < 36) // header below+ctorNb
|
if (decrypted_data.Length < 36) // header below+ctorNb
|
||||||
throw new ApplicationException($"Decrypted packet too small: {decrypted_data.Length}");
|
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);
|
using var reader = new TL.BinaryReader(new MemoryStream(decrypted_data), this);
|
||||||
var serverSalt = reader.ReadInt64(); // int64 salt
|
var serverSalt = reader.ReadInt64(); // int64 salt
|
||||||
var sessionId = reader.ReadInt64(); // int64 session_id
|
var sessionId = reader.ReadInt64(); // int64 session_id
|
||||||
var msgId = reader.ReadInt64(); // int64 message_id
|
var msgId = reader.ReadInt64(); // int64 message_id
|
||||||
var seqno = reader.ReadInt32(); // int32 msg_seqno
|
var seqno = reader.ReadInt32(); // int32 msg_seqno
|
||||||
var length = reader.ReadInt32(); // int32 message_data_length
|
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
|
if (_lastRecvMsgId == 0) // resync ServerTicksOffset on first message
|
||||||
_dcSession.ServerTicksOffset = (msgId >> 32) * 10000000 - DateTime.UtcNow.Ticks + 621355968000000000L;
|
_dcSession.ServerTicksOffset = (msgId >> 32) * 10000000 - DateTime.UtcNow.Ticks + 621355968000000000L;
|
||||||
var msgStamp = MsgIdToStamp(_lastRecvMsgId = msgId);
|
var msgStamp = MsgIdToStamp(_lastRecvMsgId = msgId);
|
||||||
|
|
@ -401,15 +416,7 @@ namespace WTelegram
|
||||||
if (_saltChangeCounter >= 30)
|
if (_saltChangeCounter >= 30)
|
||||||
throw new ApplicationException($"Server salt changed too often! Security issue?");
|
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 ((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();
|
var ctorNb = reader.ReadUInt32();
|
||||||
if (ctorNb != Layer.BadMsgCtor && (msgStamp - DateTime.UtcNow).Ticks / TimeSpan.TicksPerSecond is > 30 or < -300)
|
if (ctorNb != Layer.BadMsgCtor && (msgStamp - DateTime.UtcNow).Ticks / TimeSpan.TicksPerSecond is > 30 or < -300)
|
||||||
|
|
|
||||||
|
|
@ -276,7 +276,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
|
||||||
|
|
||||||
private static byte[] AES_IGE_EncryptDecrypt(Span<byte> input, byte[] aes_key, byte[] aes_iv, bool encrypt)
|
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/
|
// code adapted from PHP implementation found at https://mgp25.com/AESIGE/
|
||||||
var output = new byte[input.Length];
|
var output = new byte[input.Length];
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,41 @@ namespace WTelegram
|
||||||
internal int DcID => DataCenter?.id ?? 0;
|
internal int DcID => DataCenter?.id ?? 0;
|
||||||
internal IPEndPoint EndPoint => DataCenter == null ? null : new(IPAddress.Parse(DataCenter.ip_address), DataCenter.port);
|
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; }
|
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;
|
public DateTime SessionStart => _sessionStart;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue