Discussion:
[Freeswitch-users] Outbound call limit bypass prevention
Dmitry Sytchev
2014-03-03 09:09:02 UTC
Permalink
Hi all!

<intro>
If you use FS or other softswitch with to provide services with limited
amounts of concurrent calls and have enabled REFER method for your SIP
users (you may disable it by *disable-transfer* in profiles, default
false), your setup may be vulnerable to call limit bypass attack.

For example, someone stoles credentials or bruteforces account password of
your SIP user and sends an amount of calls to premium numbers or
longdistance directions. These attempts should be at least limited with
customer call limit, but there is a method to bypass it.

When attacker sees the call limit is set on account, and call limit > 1, he
tries to bypass it to increase speed and amount of outbound calls. He makes
one outbound call, then makes second. At this moment there is four channels
- two inbound channels to FS, and two outbound channels from FS to PSTN or
SIP gw. FS sets call limit usage to 2.
Now attacker issuing transfer request by sending REFER to bridge two
outbound channels with each other. After successful transfer, two inbound
channels are destroyed and outbound channels are bridged together. After
that, FS decreases call limit, since it destroyed channels limit was set on.
</intro>

The problem is the method people often use to limit concurrent calls.
Something like that:

<action application="limit" data="hash OutboundLimit
${pbx_subscriber_customer_id} ${pbx_customer_call_limit} !CALL_REJECTED"/>
<action application="bridge"
data="{ignore_early_media=false,hangup_after_bridge=false}sofia/gateway/gw/$1"/>

Now limit is set on inbound channel. When attacker bridges outbound
channels, their A-legs (inbound channels) get destroyed and FS decreases
limit due to channel destruction. So attacker have 2 outbound bridged
channels, and limit is still 0. Then the process repeats, allowing attacker
effectively bypass your call limits.

One of possible solutions is to move limits on B-legs after answer, while
keeping limits on A-legs too in order prevent exceeding limit by unanswered
calls:

We set limit on A-LEG, then on answer we decrease limit on A-LEG and "move"
it to B-leg by calling limit on outbound channel.

<action application="export" data="nolocal:execute_on_answer_1=set
api_result=${uuid_limit_release(${uuid} hash OutboundLimit
${pbx_subscriber_customer_id})}"/>
<action application="export" data="nolocal:execute_on_answer_2=limit hash
OutboundLimit ${pbx_subscriber_customer_id} ${pbx_customer_call_limit}
!CALL_REJECTED"/>
<action application="limit" data="hash OutboundLimit
${pbx_subscriber_customer_id} ${pbx_customer_call_limit} !CALL_REJECTED"/>
<action application="bridge"
data="{ignore_early_media=false,hangup_after_bridge=false}sofia/gateway/gw/$1"/>

Now limit is correctly evaluated for non-answered outbound calls, and
doesn't allow excessive calls.

Hope this helps somebody. Maybe you guys know more effective and correct
ways to prevent limit bypassing in this scenario?
--
Best regards,

Dmitry Sytchev,
IT Engineer
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.freeswitch.org/pipermail/freeswitch-users/attachments/20140303/de9d9b19/attachment.html
Brian West
2014-03-04 04:06:10 UTC
Permalink
Or you could just set limit_ignore_transfer=false

https://wiki.freeswitch.org/wiki/Limit#Variables_that_affect_limit

limit_state_handler in switch_limit.c


--
Brian West
brian at freeswitch.org
FreeSWITCH Solutions, LLC
PO BOX 2531
Brookfield, WI 53008-2531
Twitter: @FreeSWITCH , @briankwest
http://www.freeswitchbook.com
http://www.freeswitchcookbook.com

T: +1.918.420.9001 | F: +1.918.420.9002 | M: +1.918.424.WEST
iNUM: +883 5100 1420 9001
ISN: 410*543
Skype:briankwest
PGP Key: http://www.bkw.org/key.txt (AB93356707C76CED)
Post by Dmitry Sytchev
Hi all!
<intro>
If you use FS or other softswitch with to provide services with limited amounts of concurrent calls and have enabled REFER method for your SIP users (you may disable it by disable-transfer in profiles, default false), your setup may be vulnerable to call limit bypass attack.
For example, someone stoles credentials or bruteforces account password of your SIP user and sends an amount of calls to premium numbers or longdistance directions. These attempts should be at least limited with customer call limit, but there is a method to bypass it.
When attacker sees the call limit is set on account, and call limit > 1, he tries to bypass it to increase speed and amount of outbound calls. He makes one outbound call, then makes second. At this moment there is four channels - two inbound channels to FS, and two outbound channels from FS to PSTN or SIP gw. FS sets call limit usage to 2.
Now attacker issuing transfer request by sending REFER to bridge two outbound channels with each other. After successful transfer, two inbound channels are destroyed and outbound channels are bridged together. After that, FS decreases call limit, since it destroyed channels limit was set on.
</intro>
<action application="limit" data="hash OutboundLimit ${pbx_subscriber_customer_id} ${pbx_customer_call_limit} !CALL_REJECTED"/>
<action application="bridge" data="{ignore_early_media=false,hangup_after_bridge=false}sofia/gateway/gw/$1"/>
Now limit is set on inbound channel. When attacker bridges outbound channels, their A-legs (inbound channels) get destroyed and FS decreases limit due to channel destruction. So attacker have 2 outbound bridged channels, and limit is still 0. Then the process repeats, allowing attacker effectively bypass your call limits.
We set limit on A-LEG, then on answer we decrease limit on A-LEG and "move" it to B-leg by calling limit on outbound channel.
<action application="export" data="nolocal:execute_on_answer_1=set api_result=${uuid_limit_release(${uuid} hash OutboundLimit ${pbx_subscriber_customer_id})}"/>
<action application="export" data="nolocal:execute_on_answer_2=limit hash OutboundLimit ${pbx_subscriber_customer_id} ${pbx_customer_call_limit} !CALL_REJECTED"/>
<action application="limit" data="hash OutboundLimit ${pbx_subscriber_customer_id} ${pbx_customer_call_limit} !CALL_REJECTED"/>
<action application="bridge" data="{ignore_early_media=false,hangup_after_bridge=false}sofia/gateway/gw/$1"/>
Now limit is correctly evaluated for non-answered outbound calls, and doesn't allow excessive calls.
Hope this helps somebody. Maybe you guys know more effective and correct ways to prevent limit bypassing in this scenario?
--
Best regards,
Dmitry Sytchev,
IT Engineer
_________________________________________________________________________
consulting at freeswitch.org
http://www.freeswitchsolutions.com
FreeSWITCH-powered IP PBX: The CudaTel Communication Server
http://www.cudatel.com
Official FreeSWITCH Sites
http://www.freeswitch.org
http://wiki.freeswitch.org
http://www.cluecon.com
FreeSWITCH-users mailing list
FreeSWITCH-users at lists.freeswitch.org
http://lists.freeswitch.org/mailman/listinfo/freeswitch-users
UNSUBSCRIBE:http://lists.freeswitch.org/mailman/options/freeswitch-users
http://www.freeswitch.org
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 841 bytes
Desc: Message signed with OpenPGP using GPGMail
Url : http://lists.freeswitch.org/pipermail/freeswitch-users/attachments/20140303/7ba2754c/attachment.bin
Dmitry Sytchev
2014-03-04 08:15:56 UTC
Permalink
I did't dig into sources, but I actually tried both true and false.
According to wiki:
limit_ignore_transfer=true - causes the current call count to not be reset
when the call is transferred. This is useful where calls that come into a
gateway are transferred to an extension, but you want to preserve the call
count.

limit_ignore_transfer=false - calls that are transferred cause the call
count for that realm_id to decrement

So "false" is definitely not the right thing in my case. Docs are
incorrect, or I misunderstand something?
Post by Brian West
Or you could just set limit_ignore_transfer=false
https://wiki.freeswitch.org/wiki/Limit#Variables_that_affect_limit
limit_state_handler in switch_limit.c
--
Brian West
brian at freeswitch.org
FreeSWITCH Solutions, LLC
PO BOX 2531
Brookfield, WI 53008-2531
http://www.freeswitchbook.com
http://www.freeswitchcookbook.com
T: +1.918.420.9001 | F: +1.918.420.9002 | M: +1.918.424.WEST
iNUM: +883 5100 1420 9001
ISN: 410*543
Skype:briankwest
PGP Key: http://www.bkw.org/key.txt (AB93356707C76CED)
Post by Dmitry Sytchev
Hi all!
<intro>
If you use FS or other softswitch with to provide services with limited
amounts of concurrent calls and have enabled REFER method for your SIP
users (you may disable it by disable-transfer in profiles, default false),
your setup may be vulnerable to call limit bypass attack.
Post by Dmitry Sytchev
For example, someone stoles credentials or bruteforces account password
of your SIP user and sends an amount of calls to premium numbers or
longdistance directions. These attempts should be at least limited with
customer call limit, but there is a method to bypass it.
Post by Dmitry Sytchev
When attacker sees the call limit is set on account, and call limit > 1,
he tries to bypass it to increase speed and amount of outbound calls. He
makes one outbound call, then makes second. At this moment there is four
channels - two inbound channels to FS, and two outbound channels from FS to
PSTN or SIP gw. FS sets call limit usage to 2.
Post by Dmitry Sytchev
Now attacker issuing transfer request by sending REFER to bridge two
outbound channels with each other. After successful transfer, two inbound
channels are destroyed and outbound channels are bridged together. After
that, FS decreases call limit, since it destroyed channels limit was set on.
Post by Dmitry Sytchev
</intro>
The problem is the method people often use to limit concurrent calls.
<action application="limit" data="hash OutboundLimit
${pbx_subscriber_customer_id} ${pbx_customer_call_limit} !CALL_REJECTED"/>
Post by Dmitry Sytchev
<action application="bridge"
data="{ignore_early_media=false,hangup_after_bridge=false}sofia/gateway/gw/$1"/>
Post by Dmitry Sytchev
Now limit is set on inbound channel. When attacker bridges outbound
channels, their A-legs (inbound channels) get destroyed and FS decreases
limit due to channel destruction. So attacker have 2 outbound bridged
channels, and limit is still 0. Then the process repeats, allowing attacker
effectively bypass your call limits.
Post by Dmitry Sytchev
One of possible solutions is to move limits on B-legs after answer,
while keeping limits on A-legs too in order prevent exceeding limit by
Post by Dmitry Sytchev
We set limit on A-LEG, then on answer we decrease limit on A-LEG and
"move" it to B-leg by calling limit on outbound channel.
Post by Dmitry Sytchev
<action application="export" data="nolocal:execute_on_answer_1=set
api_result=${uuid_limit_release(${uuid} hash OutboundLimit
${pbx_subscriber_customer_id})}"/>
Post by Dmitry Sytchev
<action application="export" data="nolocal:execute_on_answer_2=limit
hash OutboundLimit ${pbx_subscriber_customer_id} ${pbx_customer_call_limit}
!CALL_REJECTED"/>
Post by Dmitry Sytchev
<action application="limit" data="hash OutboundLimit
${pbx_subscriber_customer_id} ${pbx_customer_call_limit} !CALL_REJECTED"/>
Post by Dmitry Sytchev
<action application="bridge"
data="{ignore_early_media=false,hangup_after_bridge=false}sofia/gateway/gw/$1"/>
Post by Dmitry Sytchev
Now limit is correctly evaluated for non-answered outbound calls, and
doesn't allow excessive calls.
Post by Dmitry Sytchev
Hope this helps somebody. Maybe you guys know more effective and correct
ways to prevent limit bypassing in this scenario?
Post by Dmitry Sytchev
--
Best regards,
Dmitry Sytchev,
IT Engineer
_________________________________________________________________________
consulting at freeswitch.org
http://www.freeswitchsolutions.com
FreeSWITCH-powered IP PBX: The CudaTel Communication Server
http://www.cudatel.com
Official FreeSWITCH Sites
http://www.freeswitch.org
http://wiki.freeswitch.org
http://www.cluecon.com
FreeSWITCH-users mailing list
FreeSWITCH-users at lists.freeswitch.org
http://lists.freeswitch.org/mailman/listinfo/freeswitch-users
UNSUBSCRIBE:http://lists.freeswitch.org/mailman/options/freeswitch-users
http://www.freeswitch.org
_________________________________________________________________________
consulting at freeswitch.org
http://www.freeswitchsolutions.com
FreeSWITCH-powered IP PBX: The CudaTel Communication Server
http://www.cudatel.com
Official FreeSWITCH Sites
http://www.freeswitch.org
http://wiki.freeswitch.org
http://www.cluecon.com
FreeSWITCH-users mailing list
FreeSWITCH-users at lists.freeswitch.org
http://lists.freeswitch.org/mailman/listinfo/freeswitch-users
UNSUBSCRIBE:http://lists.freeswitch.org/mailman/options/freeswitch-users
http://www.freeswitch.org
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.freeswitch.org/pipermail/freeswitch-users/attachments/20140304/aca6c257/attachment.html
Travis Cross
2014-03-18 16:36:06 UTC
Permalink
limit_ignore_transfer=true - causes the current call count to not be reset when the call is transferred. This is useful where calls that come into a gateway are transferred to an extension, but you want to preserve the call count.
limit_ignore_transfer=false - calls that are transferred cause the call count for that realm_id to decrement
So "false" is definitely not the right thing in my case. Docs are incorrect, or I misunderstand something?
The limit definitely needs to be set on the b-leg, and in most
circumstances you should indeed also set on the b-leg
limit_ignore_transfer=true. Maybe we should make that the default.
Travis Cross
2014-03-18 16:17:36 UTC
Permalink
Post by Dmitry Sytchev
<action application="limit" data="hash OutboundLimit ${pbx_subscriber_customer_id} ${pbx_customer_call_limit} !CALL_REJECTED"/>
<action application="bridge" data="{ignore_early_media=false,hangup_after_bridge=false}sofia/gateway/gw/$1"/>
Now limit is set on inbound channel. When attacker bridges outbound
channels, their A-legs (inbound channels) get destroyed and FS
decreases limit due to channel destruction. So attacker have 2
outbound bridged channels, and limit is still 0. Then the process
repeats, allowing attacker effectively bypass your call limits.
One of possible solutions is to move limits on B-legs after answer,
while keeping limits on A-legs too in order prevent exceeding limit
We set limit on A-LEG, then on answer we decrease limit on A-LEG and
"move" it to B-leg by calling limit on outbound channel.
<action application="export" data="nolocal:execute_on_answer_1=set api_result=${uuid_limit_release(${uuid} hash OutboundLimit ${pbx_subscriber_customer_id})}"/>
<action application="export" data="nolocal:execute_on_answer_2=limit hash OutboundLimit ${pbx_subscriber_customer_id} ${pbx_customer_call_limit} !CALL_REJECTED"/>
<action application="limit" data="hash OutboundLimit ${pbx_subscriber_customer_id} ${pbx_customer_call_limit} !CALL_REJECTED"/>
<action application="bridge" data="{ignore_early_media=false,hangup_after_bridge=false}sofia/gateway/gw/$1"/>
This is approximately correct. Note that you could use api_on_answer
for the API call there, and you could use arrays ("push") instead of
the _N suffixes.

There is a race condition here; the resource could go over-limit
between being released on the a-leg and being reacquired on the b-leg.
That wouldn't be a big deal for fraud control, but it would mean the
a-leg user might not be appropriately notified. The correct solution
here may be that we need to add a uuid_limit_exchange function to
atomically move a limit between legs.
Loading...