Pwnage 2.0

From The Apple Wiki
Pwnage 2.0
Vulnerability in SecureROM
Vulnerable versionsRev.2
Fixed in version240.4
Disclosed20 July 2008[1]
Discovered byiPhone Dev Team

This exploit in the S5L8900 bootrom is really the ultimate exploit, since it allows unsigned code to be run at the lowest level. It is available in all devices that use the S5L8900 - the iPhone, iPod Touch and iPhone 3G. It is also available on some non-iOS iPods, including the iPod nano (4th generation).


After performing header validation (SHA1 & AES), the bootrom parses the certificates present at the footer of an uploaded IMG1. These certificates are plain DER-encoded ASN.1 X.509 certificates, and thus their parsing logic is quite complex.

The bootrom has a few complex structures used to handle the parsing, most notably every ASN.1 type parsed (X.509 Certificate/TBSCertificate and X.509 Name). Each one of the types is parsed based on a common function (der::parse) called with a different der::step array.

A der::step looks as follows:

struct der::step {
	uint32 asn1_tag;
	uint8 match_content_length;
	void *visitor_or_content;
	int step_depth;
	int step_breadth;
	uint flags;

With flags being a bitmap of:

#define FLAG_VISIT_ALL 4

der::parse streams (linearly) every DER tag/field present in the given blob, and in parallel walks through the array of der::steps. It matches the tag from the blob against the asn1_tag field of the der::step. If they don't match (and FLAG_OPTIONAL isn't set), the whole parse fails. If they do match, the following action is taken:

  • If FLAG_CHECK_CONTENT_ONLY is set, the content of the streamed field is compared against visitor_or_content interpreted as a byte array of match_content_length bytes. If the content doesn't match, the parse fails.
  • If FLAG_VISIT_CONTENT_ONLY is set, visitor_or_content is interpreted as a function pointer and called with the content of the ASN.1 field being currently visited. If the visitor returns an error, the parse fails.
  • If FLAG_VISIT_ALL is set, visitor_or_content is interpreted as a function pointer and called with the entire ASN.1 field (including tag and length bytes). If the visitor returns an error, the parse fails.

Then, an ASN.1 field tree traversal action is performed:

  • If step_depth != -1, the current field extents and current step are pushed on an internal stack, and the inner contents of the field continue being parsed starting at der::steps[step_depth]. If the internal stack overflows, the parse fails.
  • If step_breadth != -1, the current field is skipped and the next field continues to be parsed by der::steps[step_breadth].
  • Otherwise (both step_depth == -1 and step_breadth == -1), the internal stack is 'popped' and both the DER streaming and der::step is restored to whatever was saved on the internal stack. The der::step that was pushed has its step_breadth consulted for the next step to be executed, if that is also -1 then the stack continues to be popped. The parse fails if the stack underflows.

As the parse continues through the DER byte stream and the der::steps, a structure is constructed and populated with information retrieved from the certificate by the visitors. For example, the signatureAlgorithm field is recorded, the entire TBSCertificate structure extents are recorded, etc. After the parse of the certificate is done, two more der::parse executions happen: on the issuerName and subjectName as recorded by the certificate visitors.

Certificate Parsing Bug[edit]

Most of the visitors in der::steps take care to never trust the lengths specified in the DER stream. However, one visitor (Certificate der::step[29]) is an exception - it copies over data from the expected signatureValue field in Certificate into the structure holding parsed certificate data without checking for maximum length.

Exploiting the bug[edit]

Offsets are those of the iPod nano (4th generation) S5L8720 bootrom. Every bootrom will likely have slightly different offsets.

The target structure (der::cert::parse_ctx) looks as follows:

struct der::cert::parse_ctx {
	uint tbs_certificate_len;
	byte *tbs_certificate_data;
	uint version;
	uint algorithm_len;
	byte *algorithm_data;
	uint issuer_len;
	byte *issuer_data;
	uint subject_len;
	byte *subject_data;
	uint extension_oid_len;
	byte *extension_oid_data;
	byte extension_critical;
	der::cert::certificate *certobj;

The signatureValue is copied to ctx->certobj->signatureValue; der::cert::certificate looks as follows:

struct der::cert::certificate {
	byte unimportant[1016]; // parsed certificate fields
	byte authorityKeyIdentifier[20];
	byte signatureValue[256];
	uint signatureValue_len;
	uint der_outer_sig_alg_type;
	byte sha1_tbs_calculated[20];
	byte sha1_all_calculated[20];
	uint unknown;

Now, certobj in der::cert::parse_ctx is a pointer. Where is the data actually held? It's in yet another object, der::chain::parse_ctx, which is the overarching structure used to parse the entire certificate chain (three certificates):

struct der::chain::parse_ctx {
	uint unknown[2];
	der::cert::parse_ctx current_cert;
	der::cert::certificate chain_certs[3];

For every certificate in the chain, cert::der::parse is called on der::chain::parse_ctx->current_cert, whose certobj is populated by one of chain_certs (each one after the other as the chain is parsed). Due to earlier checks, three certificates must be present in the footer for them to be parsed at all.

Finally, where is der::chain::parse_ctx stored? On the stack! In fact, directly after der::chain::parse_ctx there are 0x24 bytes of saved registers, with the last 4 bytes being the saved LR.

Thus, to mount the attack, we need to do the following:

  1. Present the BootROM with a valid image header with some certificates after the body. The body never gets to be checked or decrypted, so we can write anything we want there (as long as the sizes match the sizes in the header).
  2. Provide three certificates in the chain that match the bare minimum required by the certificate DER parse steps.
  3. Make the last certificate's signatureValue overflow into the saved LR. The original buffer is 256 bytes, we need to overflow it by 308 bytes (256 + 52) to leave the der::chain::parse_ctx structure, then by 0x20 more bytes to reach the saved LR, then provide 4 bytes of PC to override. Since the signatureValue is an ASN.1 BIT STRING, we need to prefix the tag value with a zero. This gives us in total 344 or 345 bytes to fill signatureValue with.

If we can't generate arbitrary image headers to set arbitrary footer certificate sizes we need to pad all certificates involved so that the signatureValue of the last cert is exactly the size we want to overflow (or at least not too long so that the copy doesn't cause a write to unmapped memory). Afterwards, with code exec, we can use the hardware AES engine to sign arbitrary headers to not have to worry about sizes.

Crafting the certificates is an exercise left to the reader. Maybe the exact constraints and process will be listed here at some point, but starting out with certificates from a legitimate WTF file and mangling the last certificate to overflow by exactly 344 bytes is a good start (possibly adjusting previous certs to make some space for the longer signatureValue).


The easiest place to stuff the payload is in the body of the image. The bootrom never gets to checking or decrypting it, so we can easily just put some executable code there. Depending on the bootrom, the image body will be placed somewhere in the beginning of SRAM (0x22000600 for iPod nano (4th generation)). Then, our stack smash can simply point to that address and we get code execution.



Exploit description is reproduced from's article on Pwnage 2.0 with permission, licensed under Creative Commons Attribution 2.0.