Lompat ke konten Lompat ke sidebar Lompat ke footer

Format Injection Vulnerability in Duo Security Web SDK


Format Injection is not a new bug, but it was never described as a subclass of A1 Injection. You probably already hate me for giving it a name (at least I didn’t create a logo!) but calling it an “injection” is too general.

The idea is very similar to SQL injections but instead of breaking quotes ' with user input and changing the query, we are breaking custom delimiters /:|,;& and changing the signed data.



  1. The user logs in the client app with valid username/password and receives TX token requesting 2nd factor authentication and APP token proving that the user actually passed first authentication step.
  2. Now the user exchanges TX token for AUTH token with Duo API (using Duo Push, SMS or a phone call). AUTH token proves the user successfully passed second authentication step.
  3. With previously obtained APP token and AUTH token the user is sent to /final_login endpoint which makes sure both tokens are valid and belong to the same username. The verify_response method returns username and you now can log the visitor in the account with returned username.


The system is built in a way that even if SKEY is leaked the attackers won’t be able to log in arbitrary accounts because they don’t have your secret AKEY and they can’t forge valid APP tokens. We found a neat format injection in a way Duo signs the APP token. Advisory ID: DUO-PSA-2015-001



The severity is low because to sign into arbitrary account you still need a valid AUTH token which means you need to compromise the SKEY. However if your app is affected don’t hesitate to rotate your AKEY.


Conditions: the victim uses Ruby, PHP, Perl, Java or ColdFusion SDKs. Also pipe symbol is allowed in usernames and username field is used as Duo ID (it can be user id or email, where pipe symbol is impossible).

See this piece of code from their Ruby library:



If we manage to create a user with username=victim||9999999999 the APP token we get for it will be parsed the same way as token for the user with username=victim.







If it’s still not clear look at the concatenated strings:

The app signs victim|IKEY|12345678 for the victim user. user, ikey, exp = string.split('|') returns user=victim and exp=12345678

The app signs victim||9999999999|IKEY|12345678 for the attacker. user, ikey, exp = string.split('|') returns user=victim and exp=9999999999 (the token is valid forever).

More examples

  1. Anything like val+DELIMITER+user_input+DELIMITER+... or [user_input,val2,val3].join(':') is very likely to be vulnerable to format injection.
  2. openURL('http://oauth/?client_id=1&client_secret=2&code='+params[:unescaped_code]) in hand-made API and OAuth implementations. Injecting code=&client_id=new_client_id&client_secret=new_client_secret leads to replacing client credentials with new values and authentication bypass.
  3. '{"val":"'+user_input+'"}' or '<xml>'+user_input+'</xml>', because XSS is a format injection too.
  4. Many payment gateways with custom data formats. This is how Liqpay and WalletOne signs orders (no delimiters, just sorted alphabetically and concatenated. I am not even going to comment that)
  5.