Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the option to check the source address of ClientHello message on DTLS-SRTP #4261

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

trengginas
Copy link
Member

There is a possible issue with DTLS “ClientHello” Race Conditions leading to DDoS attack.
This is done by sending a malicious DTLS ClientHello message from any
IP address to the expected port, potentially causing a “network race condition” if the malicious
message is processed before the legitimate one.
This patch will add the option (PJMEDIA_SRTP_DTLS_CHECK_HELLO_ADDR) to enable checking the source address of the "ClientHello" message is coming from a valid source.

Notes:

  • On ICE, the valid address will be available after the negotiation is done, so any "ClientHello" message received prior to it might get ignored.

@trengginas trengginas added this to the release-2.16 milestone Jan 22, 2025
@trengginas trengginas self-assigned this Jan 22, 2025
nanangizz
nanangizz previously approved these changes Jan 23, 2025
Copy link
Member

@nanangizz nanangizz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure this is tested with various scenarios, e.g:

  • with & without ICE,
  • ICE chooses TURN & STUN/host as valid candidate.

*
* Default value: 0
*/
#ifndef PJMEDIA_SRTP_DTLS_CHECK_HELLO_ADDR
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a compile-time setting? and why is the default 0 if it may cause a security issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might add a slight delay in starting the handshake (wait for the transport address and RTP source address to be available) and possible issues when using aggressive nomination.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the pdf, I believe the address check should be made mandatory for ICE, and the address should match one of the verified (valid?) candidates obtained during ICE check, so there should be no delay because ICE check has been done.

For no-ICE, then you can use the compile-time setting, because the best we can do is compare it with the SDP address, which may not be accurate.

@trengginas
Copy link
Member Author

Please make sure this is tested with various scenarios, e.g:

  • with & without ICE,
  • ICE chooses TURN & STUN/host as valid candidate.

When using this option, it is recommended to use regular nomination for ICE. When enabling STUN with aggressive nomination might result in a different candidate pair type being used between endpoints and preventing the handshake.

@trengginas
Copy link
Member Author

This is the case when using ICE with STUN (aggressive nomination):
caller side:

14:13:01.139        icetp00  ICE negotiation success after 0s:055
14:13:01.139        icetp00   Comp 1: sending from srflx candidate 117.90.81.147:12322 to srflx candidate 117.90.81.147:12325
14:13:01.141        icetp00   Comp 2: sending from host candidate 192.168.100.51:9938 to host candidate 192.168.100.51:4003
...
14:13:02.177   dtls0326C288 !DTLS-SRTP RTCP channel ignoring 222 bytes, src addr [117.90.81.147:12327], expecting from [192.168.100.51:4003]
14:13:02.201   dtls0326C288  DTLS-SRTP negotiation for RTP channel completed!

===

callee side:

14:13:01.142        icetp00  ICE negotiation success after 0s:069
14:13:01.143        icetp00   Comp 1: sending from srflx candidate 117.90.81.147:12325 to srflx candidate 117.90.81.147:12322
14:13:01.143        icetp00   Comp 2: sending from srflx candidate 117.90.81.147:12327 to srflx candidate 117.90.81.147:12324

===

@nanangizz
Copy link
Member

For trickle ICE, the candidates may be added rapidly while app is allowed to send/recv data before the ICE nego is completed. So if the proposed feature is on, perhaps it should indeed check the source address against all remote candidates. This may be applicable for aggressive mode too.

- The check will always be performed when ICE is use
- Check the RTP/RTPC address against the candidate when ICE is use
@nanangizz
Copy link
Member

So now, does it work for aggressive & trickle modes?

@trengginas
Copy link
Member Author

So now, does it work for aggressive & trickle modes?

Yes, now working with aggressive and trickle

@sauwming sauwming requested a review from nanangizz January 30, 2025 01:13
@sauwming
Copy link
Member

For ICE:
It seems that enum_cands() will enumerate all candidates regardless of the candidate status (even the failed ones?). The recommendation is to compare it with verified candidates (i.e. the candidates that have passed the media consent verification phase).
So there are two options:
a. modify the API to return only verified candidates: enum_verified_cands(), or
b. let ICE handle the verification: pjmedia_ice_verify_remote_cand()

If no ICE is used:
The recommendation is to compare it with the SDP address.

@sauwming sauwming dismissed nanangizz’s stale review January 30, 2025 01:41

new modification

@nanangizz
Copy link
Member

For ICE: It seems that enum_cands() will enumerate all candidates regardless of the candidate status (even the failed ones?). The recommendation is to compare it with verified candidates (i.e. the candidates that have passed the media consent verification phase). So there are two options: a. modify the API to return only verified candidates: enum_verified_cands(), or b. let ICE handle the verification: pjmedia_ice_verify_remote_cand()

It looks like "verified" is specific to the recommendation, not really ICE things?
If it does, adding pair-check-status-info for each candidate should be more natural than adding "verified" APIs to ICE.
Then it is app/DTLS responsibility to "translate" pair-check-status to "verified".

@sauwming
Copy link
Member

It looks like "verified" is specific to the recommendation, not really ICE things?

Correct. ICE itself doesn't use the term "verified", and the document explains it in the media consent verification phase, which means that that the other party is the peer they claim to be. So this way, security can be achieved.

If it does, adding pair-check-status-info for each candidate should be more natural than adding "verified" APIs to ICE. Then it is app/DTLS responsibility to "translate" pair-check-status to "verified".

Yes, this is fine too. So, it means that enum_cands() will also return the status of the candidates, right?

@nanangizz
Copy link
Member

Yes, this is fine too. So, it means that enum_cands() will also return the status of the candidates, right?

Yes. Note that in ICE, the status belongs to pair-check instead of candidate, a remote candidate may be paired (& checked) against multiple local candidates. If DTLS just needs to list remote candidates that has been checked, it can be done but needs to be clarified in the docs.

@trengginas
Copy link
Member Author

There's an issue when using aggressive nomination/trickle:
The controlled and controlling side might use different candidates.
Controlling side:

15:15:55.315  stuse03EDF9E8  .RX 120 bytes STUN message from 192.168.100.51:4008:
--- begin STUN message ---
STUN Binding request
 Hdr: length=100, magic=2112a442, tsx_id=00002c605772139d26ca369a
 Attributes:
  ICE-CONTROLLED: length=8, data=2e4013661cd0366b
--- end of STUN message ---
15:15:55.315  stuse03EDF9E8  ..TX 88 bytes STUN message to 192.168.100.51:4008:
--- begin STUN message ---
STUN Binding success response
 Hdr: length=68, magic=2112a442, tsx_id=00002c605772139d26ca369a
--- end of STUN message ---

15:15:55.323        icetp00  Starting checklist periodic check
15:15:55.323        icetp00  .Sending connectivity check for check 1: [2] 192.168.100.51:9933-->100.100.100.100:21760
15:15:55.323  stuse03EE0F50  ...TX 124 bytes STUN message to 100.100.100.100:21760:
--- begin STUN message ---
STUN Binding request
 Hdr: length=104, magic=2112a442, tsx_id=00002d906e5d1ad445091239
 Attributes:
  USE-CANDIDATE: length=0
  ICE-CONTROLLING: length=8, data=0124305e440d491c
--- end of STUN message ---
15:15:55.323   utsx03F09E00  ...STUN client transaction created
15:15:55.323   utsx03F09E00  ...STUN sending message (transmit count=1)
15:15:55.324        icetp00  ..Check 1: [2] 192.168.100.51:9933-->100.100.100.100:21760: state changed from Waiting to In Progress
15:15:55.340  stuse03EE0F50 !.RX 120 bytes STUN message from 100.100.100.100:21760:
--- begin STUN message ---
STUN Binding request
 Hdr: length=100, magic=2112a442, tsx_id=00002c607049692c26ca369b
 Attributes:
  ICE-CONTROLLED: length=8, data=2e4013661cd0366b
--- end of STUN message ---
15:15:55.340  stuse03EE0F50  ..TX 88 bytes STUN message to 100.100.100.100:21760:
--- begin STUN message ---
STUN Binding success response
 Hdr: length=68, magic=2112a442, tsx_id=00002c607049692c26ca369b
 Attributes:
--- end of STUN message ---
15:15:55.341        icetp00  .Triggered check for check 1 not performed because it's in progress. Retransmitting
15:15:55.341   utsx03F09E00  ..STUN sending message (transmit count=1)
15:15:55.345        icetp00  Starting checklist periodic check
15:15:55.345        icetp00  .Sending connectivity check for check 3: [2] 192.168.100.51:9933-->192.168.100.51:4003
15:15:55.345  stuse03EE0F50  ...TX 124 bytes STUN message to 192.168.100.51:4003:
--- begin STUN message ---
STUN Binding request
 Hdr: length=104, magic=2112a442, tsx_id=00002d9063cb6bfc4509123a
 Attributes:
  USE-CANDIDATE: length=0
  ICE-CONTROLLING: length=8, data=0124305e440d491c
--- end of STUN message ---
15:15:55.345   utsx03F08CE0  ...STUN client transaction created
15:15:55.345   utsx03F08CE0  ...STUN sending message (transmit count=1)
15:15:55.346        icetp00  ..Check 3: [2] 192.168.100.51:9933-->192.168.100.51:4003: state changed from Frozen to In Progress
15:15:55.346  stuse03EE0F50  .RX 88 bytes STUN message from 192.168.100.51:4003:
--- begin STUN message ---
STUN Binding success response
 Hdr: length=68, magic=2112a442, tsx_id=00002d9063cb6bfc4509123a
 Attributes:
--- end of STUN message ---
15:15:55.346        icetp00  .Check 3: [2] 192.168.100.51:9933-->192.168.100.51:4003 (nominated): connectivity check SUCCESS
15:15:55.346        icetp00  .Check 3: [2] 192.168.100.51:9933-->192.168.100.51:4003: state changed from In Progress to Succeeded
15:15:55.347        icetp00  .Check 3 is successful and nominated
15:15:55.347        icetp00  .Cancelling check 1: [2] 192.168.100.51:9933-->100.100.100.100:21760 (In Progress)
15:15:55.347 stun_session.c  .tdata 03F09CDC destroy request, force=0, tsx=03F09E00, destroying=0
15:15:55.347   utsx03F09E00  .STUN transaction 03F09E00 schedule destroy
15:15:55.347        icetp00  .Check 1: [2] 192.168.100.51:9933-->100.100.100.100:21760: state changed from In Progress to Failed
15:15:55.348        icetp00  .ICE process complete, status=Success
15:15:55.348        icetp00  .Valid list
15:15:55.348        icetp00  . 0: [1] 100.100.100.100:21818-->100.100.100.100:21823 (nominated, state=Succeeded)
15:15:55.348        icetp00  . 1: [2] 192.168.100.51:9933-->192.168.100.51:4003 (nominated, state=Succeeded)

Controlled side:

15:15:55.313        icetp00  .Sending connectivity check for check 2: [1] 192.168.100.51:4008-->192.168.100.51:9937
15:15:55.313  stuse035B3770  ...TX 120 bytes STUN message to 192.168.100.51:9937:
--- begin STUN message ---
STUN Binding request
 Hdr: length=100, magic=2112a442, tsx_id=00002c605772139d26ca369a
 Attributes:
  ICE-CONTROLLED: length=8, data=2e4013661cd0366b
--- end of STUN message ---

15:15:55.313   utsx035A0A4C  ...STUN client transaction created
15:15:55.313   utsx035A0A4C  ...STUN sending message (transmit count=1)
15:15:55.313        icetp00  ..Check 2: [1] 192.168.100.51:4008-->192.168.100.51:9937: state changed from Waiting to In Progress
15:15:55.315  stuse035B3770  .RX 88 bytes STUN message from 192.168.100.51:9937:
--- begin STUN message ---
STUN Binding success response
 Hdr: length=68, magic=2112a442, tsx_id=00002c605772139d26ca369a
--- end of STUN message ---

15:15:55.315        icetp00  .Check 2: [1] 192.168.100.51:4008-->192.168.100.51:9937 (not nominated): connectivity check SUCCESS
15:15:55.315        icetp00  .Check 2: [1] 192.168.100.51:4008-->192.168.100.51:9937: state changed from In Progress to Succeeded
15:15:55.315        icetp00  .Check 3: [2] 192.168.100.51:4003-->192.168.100.51:9933: state changed from Frozen to Waiting
15:15:55.316        icetp00  .Check 2 is successful
15:15:55.316 stun_session.c  .tdata 035A092C destroy request, force=0, tsx=035A0A4C, destroying=0
15:15:55.316   utsx035A0A4C  .STUN transaction 035A0A4C schedule destroy
15:15:55.317  stuse035B3770  .RX 88 bytes STUN message from 100.100.100.100:21818:
--- begin STUN message ---
STUN Binding success response
 Hdr: length=68, magic=2112a442, tsx_id=00002c6009027bb926ca3699
--- end of STUN message ---

15:15:55.317        icetp00  .Check 0: [1] 192.168.100.51:4008-->100.100.100.100:21818 (nominated): connectivity check SUCCESS
15:15:55.317        icetp00  .Check 0: [1] 192.168.100.51:4008-->100.100.100.100:21818: state changed from In Progress to Succeeded
15:15:55.318        icetp00  .Check 1: [2] 192.168.100.51:4003-->100.100.100.100:21821: state changed from Frozen to Waiting
15:15:55.318        icetp00  .Check 0 is successful and nominated
15:15:55.318 stun_session.c  .tdata 035A3844 destroy request, force=0, tsx=035A3964, destroying=0
15:15:55.318   utsx035A3964  .STUN transaction 035A3964 schedule destroy
15:15:55.335        icetp00 !Starting checklist periodic check
15:15:55.335        icetp00  .Sending connectivity check for check 1: [2] 192.168.100.51:4003-->100.100.100.100:21821
15:15:55.336  stuse035B5568  ...TX 120 bytes STUN message to 100.100.100.100:21821:
--- begin STUN message ---
STUN Binding request
 Hdr: length=100, magic=2112a442, tsx_id=00002c607049692c26ca369b
 Attributes:
  ICE-CONTROLLED: length=8, data=2e4013661cd0366b
--- end of STUN message ---

15:15:55.336   utsx035A4ECC  ...STUN client transaction created
15:15:55.336   utsx035A4ECC  ...STUN sending message (transmit count=1)
15:15:55.336        icetp00  ..Check 1: [2] 192.168.100.51:4003-->100.100.100.100:21821: state changed from Waiting to In Progress
15:15:55.346  stuse035B5568  .RX 124 bytes STUN message from 192.168.100.51:9933:
--- begin STUN message ---
STUN Binding request
 Hdr: length=104, magic=2112a442, tsx_id=00002d9063cb6bfc4509123a
 Attributes:
  USE-CANDIDATE: length=0
  ICE-CONTROLLING: length=8, data=0124305e440d491c
--- end of STUN message ---

15:15:55.346  stuse035B5568  ..TX 88 bytes STUN message to 192.168.100.51:9933:
--- begin STUN message ---
STUN Binding success response
 Hdr: length=68, magic=2112a442, tsx_id=00002d9063cb6bfc4509123a
--- end of STUN message ---

15:15:55.346        icetp00  .Performing triggered check for check 3
15:15:55.346        icetp00  ..Sending connectivity check for check 3: [2] 192.168.100.51:4003-->192.168.100.51:9933
15:15:55.347  stuse035B5568  ....TX 120 bytes STUN message to 192.168.100.51:9933:
--- begin STUN message ---
STUN Binding request
 Hdr: length=100, magic=2112a442, tsx_id=00002c604a80187e26ca369c
 Attributes:
  ICE-CONTROLLED: length=8, data=2e4013661cd0366b
--- end of STUN message ---

15:15:55.347   utsx035A1724  ....STUN client transaction created
15:15:55.347   utsx035A1724  ....STUN sending message (transmit count=1)
15:15:55.347        icetp00  ...Check 3: [2] 192.168.100.51:4003-->192.168.100.51:9933: state changed from Waiting to In Progress
15:15:55.347  stuse035B5568  .RX 88 bytes STUN message from 100.100.100.100:21821:
--- begin STUN message ---
STUN Binding success response
 Hdr: length=68, magic=2112a442, tsx_id=00002c607049692c26ca369b
--- end of STUN message ---

15:15:55.347        icetp00  .Check 1: [2] 192.168.100.51:4003-->100.100.100.100:21821 (not nominated): connectivity check SUCCESS
15:15:55.347        icetp00  .Check 1: [2] 192.168.100.51:4003-->100.100.100.100:21821: state changed from In Progress to Succeeded
15:15:55.348        icetp00  .Check 1 is successful
15:15:55.348 stun_session.c  .tdata 035A4DAC destroy request, force=0, tsx=035A4ECC, destroying=0
15:15:55.348   utsx035A4ECC  .STUN transaction 035A4ECC schedule destroy
15:15:55.348  stuse035B5568  .RX 124 bytes STUN message from 100.100.100.100:21821:
--- begin STUN message ---
STUN Binding request
 Hdr: length=104, magic=2112a442, tsx_id=00002d906e5d1ad445091239
 Attributes:
  USE-CANDIDATE: length=0
  ICE-CONTROLLING: length=8, data=0124305e440d491c
--- end of STUN message ---

15:15:55.348  stuse035B5568  ..TX 88 bytes STUN message to 100.100.100.100:21821:
--- begin STUN message ---
STUN Binding success response
 Hdr: length=68, magic=2112a442, tsx_id=00002d906e5d1ad445091239
--- end of STUN message ---

15:15:55.348        icetp00  .Valid check 1: [2] 100.100.100.100:21760-->100.100.100.100:21821 is nominated
15:15:55.348        icetp00  .Triggered check for check 1 not performed because it's completed
15:15:55.349        icetp00  ..Check 1 is successful and nominated
15:15:55.349        icetp00  ..Cancelling check 3: [2] 192.168.100.51:4003-->192.168.100.51:9933 (In Progress)
15:15:55.349 stun_session.c  ..tdata 035A1604 destroy request, force=0, tsx=035A1724, destroying=0
15:15:55.349   utsx035A1724  ..STUN transaction 035A1724 schedule destroy
15:15:55.349        icetp00  ..Check 3: [2] 192.168.100.51:4003-->192.168.100.51:9933: state changed from In Progress to Failed
15:15:55.349        icetp00  ..ICE process complete, status=Success
15:15:55.349        icetp00  ..Valid list
15:15:55.350        icetp00  .. 0: [1] 100.100.100.100:21823-->100.100.100.100:21818 (nominated, state=Succeeded)
15:15:55.350        icetp00  .. 1: [2] 100.100.100.100:21760-->100.100.100.100:21821 (nominated, state=Succeeded)
15:15:55.350        icetp00  .. 2: [1] 192.168.100.51:4008-->192.168.100.51:9937 (not nominated, state=Succeeded)

Check 1 connectivity check was received late, so the controlling side will use Check 3 instead and fail the Check 1.
However on the controlled side, it uses Check 1.

@sauwming
Copy link
Member

It looks like it's caused by PJ_ICE_CANCEL_ALL (by default 1):

} else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS
&& (PJ_ICE_CANCEL_ALL ||
CMP_CHECK_PRIO(c, check) < 0)) {
/* State is IN_PROGRESS, cancel transaction */
if (c->tdata) {
LOG5((ice->obj_name,
"Cancelling check %s (In Progress)",

* If a higher priority check is In Progress, this rule would cause that
* check to be performed even when it most likely will fail.
*
* The macro here controls if ICE session should cancel all In Progress
* checks for the same component regardless of its priority.
*
* Default: 1 (yes, cancel all)
*/
#ifndef PJ_ICE_CANCEL_ALL

I don't quite understand the reasoning why we enable this setting by default though, especially about the statement even when it most likely will fail.
So perhaps try disabling this setting?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants