A few of us were hanging out in the Veracode kitchen the other day and got to discussing the idea of programmatically injecting vulnerabilities into software. This is essentially the opposite of the problem that most security vendors, including ourselves, are trying to solve -- that is, detecting vulnerabilities. Clearly there's not much business value in making software less safe, though you could imagine such a tool being used for educational purposes or a way to mass-produce QA test cases.

It sounds easy, right? Certainly it would be easy to inject the types of classic security problems that are trivially detectable. For example:

  • Replace bounded string manipulation calls with unbounded ones, e.g. strncpy() becomes strcpy(), strncat() becomes strcat(), etc.
  • Replace printf style calls that use %s format string specifiers, e.g. printf("%s", buf) becomes printf(buf)
  • Replace scanf() calls with everybody's favorite function, gets() (Hi AIX developers!)
  • Create type mismatches and potential integer coercion issues by replacing unsigned variables with their signed counterparts

Or, on the web application side:

  • Create SQL injection by replacing prepared statements with concatenated queries -- not trivial but there are a limited number of database APIs, and SQL statements follow a defined syntax, so it wouldn't be that hard
  • Inject XSS by removing all calls to known output encoding routines
  • Disable input validation by removing all calls to mechanisms such as regex replacement

Of course, when you start messing with input validation, you run the risk of altering the intended operation of the program. Maybe that regex replacement you removed was security-related, but on the other hand, maybe it's performing a transformation that's relevant to the application logic. If you didn't care about the program actually being able to function, you could:

  • Arbitrary shorten character arrays
  • Use the incorrect functions to manipulate standard and/or wide strings
  • Add or remove calls to free() or delete
  • Swap calls to delete and delete[]
  • Remove checks for null -- string contents, malloc() return values, whatever you feel like
  • Create off-by-one errors in loop counters and array indexes

I could go on forever but you probably get the point. The trick would be finding the ones that didn't make the program segfault after 30 seconds of operation. Then again, is it really important that the vulnerable version of the program behaves identically to the original under normal operating circumstances? That constraint makes it more challenging; otherwise, it's kind of a boring exercise. You could argue that it's important if you plan to use the modified version to test a fuzzer (or other dynamic analysis tool) but not for static analysis.

Either way, you'd eventually hit the same boundaries as a vulnerability detection tool. There would still be entire classes of vulnerabilities that could not be addressed effectively. How do you create authorization bypass issues without an understanding of what's being protected and from whom? How do you inject CSRF without knowing which functions are meaningful and which tokens/identifiers are safe to remove? And you can forget about business logic flaws entirely. Basically, it's hard to break something so specific without a decent understanding of how that something was designed to function in the first place.

Now that my brain is sufficiently uncluttered, I can get back to doing real work.

About Chris Eng

Chris Eng, vice president of research, is responsible for integrating security expertise into Veracode’s technology. In addition to helping define and prioritize the security feature set of the Veracode service, he consults frequently with customers to discuss and advance their application security initiatives. With over 15 years of experience in application security, Chris brings a wealth of practical expertise to Veracode.

Comments (3)

Andy Steingruebl | November 17, 2007 5:45 pm

The real trick would be to make one that exhibits one or more of these behaviors and has the same hash. Its pretty obvious when a trojan doesn't do the right then generally. Its an entirely different matter for it to behave almost identically, have the same hash, but have a few backdoors inserted.

A thought exercise only I hope - not looking forward to this being practical :)

ds | November 19, 2007 3:31 pm

You are probably already familiar with the paper "Reflections on Trusting Trust", published in 1984 by Ken Thompson (he of C).


Worth a read by those interested in this topic.

As for Commenter Andy's suggestion on making the program have an identical hash value... if that were possible, then the hash algorithm in use would be broken and (hopefully) not in use anymore.


CEng | November 20, 2007 3:29 pm

@Andy: As ds pointed out, it would be impractical to try and keep the hash the same -- that would require a major cryptographic breakthrough. The point was more along the lines of auto-generating test cases for code review tools, as opposed to creating undetectable backdoors.

If you think about it, the effort involved in creating arbitrarily complex test cases is significant. You can use synthetic cases such as SAMATE but these don't approach the level of code complexity that a real-world codebase would have. Similarly, you can benchmark yourself against publicly-disclosed vulnerabilities in real-world code but that is a labor intensive process to catalog all the data.

Please Post Your Comments & Reviews

Your email address will not be published. Required fields are marked *

Love to learn about Application Security?

Get all the latest news, tips and articles delivered right to your inbox.