Ruby-iCloud icon indicating copy to clipboard operation
Ruby-iCloud copied to clipboard

backups

Open tcurdt opened this issue 10 years ago • 212 comments

I am trying to get access to the backups but are still failing. I am trying a GET

base = result['com.apple.mobileme']['com.apple.Dataclass.Account'].url
url = base + "/mbs/" + dsPrsID
https://p05-setup.icloud.com:443/mbs/64249351

But that's giving me even a 503. So that must be wrong. Using a different host I get at least a 400.

https://p05-mobilebackup.icloud.com/mbs/64249351

tcurdt avatar Nov 08 '13 04:11 tcurdt

Hi! I stumbled upon this issue while searching "-mobilebackup.icloud.com/mbs" in google. I'm creating a full iCloud decryption program. It seems you're using an outdated url. Instead of p05-mobilebackup.icloud.com, try using "p15-mobilebackup.icloud.com" I must warn you though, once you get the response at that url, your going to to have to process it somehow. Its you're dsid (64249351 for you), a space, and then some hex. I need to make sense of this data. I know its kind of odd to ask you a question when its your issue, but since you've gotten this far, would you happen to know how this data is encrypted?

alexhulbert avatar Nov 15 '13 14:11 alexhulbert

I don't think thats an outdated URI, i think p-15 stands for partition 15 (partitioning as in http://en.wikipedia.org/wiki/Partition_(database) )

On 15 nov. 2013, at 15:42, Taconut [email protected] wrote:

Hi! I stumbled upon this issue while searching "-mobilebackup.icloud.com/mbs" in google. I'm creating a full iCloud decryption program. It seems you're using an outdated url. Instead of p05-mobilebackup.icloud.com, try using "p15-mobilebackup.icloud.com" I must warn you though, once you get the info at that url, your going to to have to process it somehow. Its you're dsid (64249351 for you), a space, and then some hex. I need to make sense of this data. I know its kind of odd to ask you a question when its your issue, but since you've gotten this far, would you happen to know how this data is encrypted?

— Reply to this email directly or view it on GitHub.

jurriaan avatar Nov 15 '13 15:11 jurriaan

I also think it's the partition - might suggest the wrong base url though. It should return a list of backup id's AFAIK.

tcurdt avatar Nov 15 '13 16:11 tcurdt

Really? But what do you do with the response? Evidently, your supposed to get a list of backup Ids to use. I Just get random hex. Has anyone tried this recently? I must be doing something wrong, because I tried multiple apple IDs. If you want to review how I'm doing this, you can see my code here

Also, as for your problem, the get_account_settings will show you the correct url for making your request.

EDIT: I've noticed that different articles have different "P" values over time, I know they grow. So it might depend on when the backup was created. Currently, your backup should either have p15 or p16

EDIT 2: Just so you know, I'm using the mobilebackup in the url, and I'll try "setup" pretty soon

alexhulbert avatar Nov 15 '13 17:11 alexhulbert

As for my problem - I am getting the "https://p05-setup.icloud.com:443/mbs/64249351" URL based from the get_account_settings :) ...but for me the request always fails with 503 or 400. You are getting at least a hex result? What's your status code?

Once we have the list of backup ids we should get the file authentication tokens and with them get the URLs for the file chunks - with a big AFAIU.

tcurdt avatar Nov 15 '13 19:11 tcurdt

Oh, I remember the fix for your problem! I set up a man-in-the-middle attack on "Elcomsoft Phone Password Breaker" by intercepting the traffic over fiddler's proxy. I was then able to see that the headers are the same as the ones used for authorization, except you have to

  1. make sure that protocol version is set to "1.7"
  2. get a new mmeAuthToken (has to be a different one) from get_account_settings
  3. use base64 like you did with get_account_settings for Authorization, but only with the new mmeAuthToken and type "X-MobileMe-AuthToken" before it instead of "Basic"

This is all a little confusing so I'll give you an example:

GET /mbs/DsPrsID HTTP/1.1 User-Agent: MobileBackup/5.1.1 (9B206; iPhone4,1) Host: p05-mobilebackup.icloud.com Accept: application/vnd.com.apple.mbs+protobuf Accept-Language: en-US X-Apple-Request-UUID: 4EFFF273-5611-479B-A945-04DA0A0F2C3A X-Apple-MBS-Protocol-Version: 1.7 X-MMe-Client-Info: <iPhone4,1> <iPhone OS;5.1.1;9B206> <com.apple.AppleAccount/1.0 (com.apple.backupd/(null))> Authorization: X-MobileMe-AuthToken base64(dsPrsID + ":" + mmeAuthToken (the one from get_accout_settings)

alexhulbert avatar Nov 15 '13 20:11 alexhulbert

The reason I'm doing all this is for a program I'm creating called "iCEW1ND" It's going to be able to backup all of your apps, data, settings, etc. without even booting up your phone (it just needs to enter DFU mode). It will also be able to restore all of that from a manual backup (using this program), from iTunes, or from iCloud.

alexhulbert avatar Nov 15 '13 20:11 alexhulbert

Thanks for the pointer!

Same here. I have lost some SMS that are still stored in an iCloud backup. I could just not believe there is no easier way get them back. So I started digging :)

tcurdt avatar Nov 15 '13 20:11 tcurdt

OH MY GOD! You're profile looked familiar, so I clicked on it. Turns out that I was planning on using your "jDeb" package for backing up custom paths. What a coincidence! Actually, would you be willing to help me with my program? I'm about 1/3 done

alexhulbert avatar Nov 15 '13 20:11 alexhulbert

Funny indeed :) Let me know if you run into issues with it.

tcurdt avatar Nov 15 '13 23:11 tcurdt

Meh! Still cannot get it to work. Could you maybe attach a request log with the tokens/ids anonymized? I don't have a man in the middle setup atm.

tcurdt avatar Nov 15 '13 23:11 tcurdt

As the header suggests the call should return a protobuffer.

tcurdt avatar Nov 16 '13 01:11 tcurdt

I've pushed a working example to the repository After you clone and bundle install you can run ruby icloud.rb And when it lands at the pry prompt type c.process RubyiCloud::BackupRequest

The next step is to figure out the protocol buffers ;)

jurriaan avatar Nov 16 '13 10:11 jurriaan

Wow! It worked! What did you do differently? Upon looking at your code, it seems it works just like I'm doing it, yet it outputs #<RubyiCloud::ProtocolBuffers::Backups psid="dsPrsID" udids=[the backup uuids] todo=false>

alexhulbert avatar Nov 16 '13 18:11 alexhulbert

I had no clue what protobuffers were, but now that I do, everything is making sense. Onward with my project! I'll let you know if I encounter any more problems along the way (I probably will) and you see my progress at github.com/Triforce1/iCEW1ND. Thanks!

EDIT: Its only been an hour and I'm stuck already :) I've been trying to get the udids from the output of icloud using protostuff. I have no clue how to approach this. Could somebody show me how to do this or at least point me in the right direction? I've got the encoded data as a byte array.

alexhulbert avatar Nov 16 '13 19:11 alexhulbert

You have to reverse engineer the protocol buffer definitions using something like protoc --decode_raw. Then you write a protocol buffer description and tweak it until it works. I don't have time to finish the implementation at the moment, but you are welcome to do so ;)

jurriaan avatar Nov 16 '13 21:11 jurriaan

Already had a first look with Charles for the man-in-the-middle. It supports protobuffer decoding.

tcurdt avatar Nov 16 '13 22:11 tcurdt

I always use mitmproxy. It's open source and I think it also supports protobuffer decoding. I've just pushed another change, you can now view information about a backup by using

c.process RubyiCloud::BackupRequest, "the backup UUID"

Hopefully it's useful ;)

jurriaan avatar Nov 16 '13 22:11 jurriaan

Sweet! I really appreciate all of this help, I should be done with this in no time.

alexhulbert avatar Nov 16 '13 22:11 alexhulbert

If you want to know more about how iCloud works, you can look at Vladmir Katalov's presentation at REcon 2013 on "Decrypting iCloud"

alexhulbert avatar Nov 17 '13 01:11 alexhulbert

I have good news! I've finished all of the Requests and I can now pull out 2 main things from the data: the keys to decrypt iCloud chunks and the file-name-to-chunk mapping data. I just need to retrieve two more things:

  1. The chunk authentication tokens
    (p##-mobilebackup.icloud.com/mbs/(dsPrsID)/(backupudid)/(snapshotid)/getFiles)
  2. The base url for downloading the files (p##-content.icloud.com/(dsPrsID)/authorizeGet)

These should be accessed using HTTP POST rather than GET, unlike the others. I'm trying to get a response, but its just returning null. I'm not sure what to do from here,

Another issue is decrypting these chunks once the've been downloaded. After looking over everything, the iCloud decryption process very closely resembles that of the iTunes method, where the manifest.mbdb in iCloud matches up to the file list and the keys are stored in a similar way.

Fortunately, there is an open-source iTunes decryptor written in Python which can be found here and here. Adapting this code may be the easiest way to decrypt iCloud backups into an easily-usable form. Do any of you have any idea how I should approach this? Sniffing and Disassembly won't help me much here...

alexhulbert avatar Nov 17 '13 23:11 alexhulbert

Looking at your code i see you don't use the /mbs/$dsid$/$udid$/$id$/listFiles url. I haven't had time to investigate backups further, but don't you need the output of that? (because I just finished the proto description and decoding of listFiles.. Would be a shame if that was all in vain...).

Oh, and btw, I'm not the one behind the jDeb package ;)

jurriaan avatar Nov 17 '13 23:11 jurriaan

Oops forgot to push that...

alexhulbert avatar Nov 17 '13 23:11 alexhulbert

There, all of the (malfunctioning) code should be here.

Wait... the listFiles is in Proto?! I checked it with protoc and it said it couldn't parse it. If it's a protobuf, than our lives our going to be much easier :) I've been trying to parse hex manually all this time.

EDIT: I actually had the listFiles method before I pushed it. I GET it at line 182 in iCloud.java

alexhulbert avatar Nov 17 '13 23:11 alexhulbert

It took me quite a while to figure it out, I used a hex editor to view the output and after a while I recognised a pattern. Apple created their own format containing multiple protobuffer streams. The first few bytes are just a Varint (most protobuffer libraries have a function to read a varint from a data stream). The varint is equal to the length of the next protobuffer data. So the output of listFiles looks like: [ Varint Data Varint Data Varint Data .. etc ]

To get the data out of it you'll have to do something like this:

until io.eof? do
  length = Varint.decode(io)
  file = decode_file_protobuffer(io.read(length)) # decode the data
end

Edit: see the latest commit for the definitions of the data in the listFiles data. I have yet to figure out the meaning of everything..

Edit2: Info about varints

jurriaan avatar Nov 17 '13 23:11 jurriaan

I figured out the meaning of this file when I was reverse-engineering the program in IDA. After the chunks are downloaded and decrypted, their names are still just random hex strings. The purpose of "listFiles" is to tell you what the chunks' real names are. Its a mapping of real name to chunk name.

alexhulbert avatar Nov 17 '13 23:11 alexhulbert

This looks fairly easy, but how can the program differentiate between the varint and file? It seems like once I figue out that, I can use com.google.protobuf.CodedInputStream.readRawVarint32 to decode the first one into a length, and so on. Would it be something like this?

fileList = <LIST DATA>;
varintLength = <THE LENGTH OF A VARINT> ; // the actual length in bytes of a varint itself
files = {}; //This is an array that will contain each file as a protobuffer
offset = 0;
index = 0;
do {
    varint = CodedInputStream.readRawVarint32(fileList.bytes[ BETWEEN offset AND offset + varintLength ]);
    //"Varint" variable is the length of the protobuf after the varint; the decoded varint value
    offset += varintLength + 1;
    files[index] = Decode_Protobuf(fileList.bytes[ BETWEEN offset AND offset + varint ];
    offset += varint;
    index = index + 1;
} while (offset < fileList.length)
PARSE EACH ITEM IN files[] AND DO STUFF WITH IT;

alexhulbert avatar Nov 18 '13 00:11 alexhulbert

The thing about variants is that they have a variable length (hence the name variant). The CodedInputStream.readRawVarint32 method reads the varint and then stops.

You could try something like this:

CodedInputStream codedFileList = new CodedInputStream(fileListIOobject);
length = codedFileList.readRawVarint32();
files[index] = Decode_Protobuf(codedFileList.readRawBytes(length));

jurriaan avatar Nov 18 '13 06:11 jurriaan

Wow - good progress here!

@jurriaan the jdeb reference was aimed at me ;)

tcurdt avatar Nov 18 '13 10:11 tcurdt

I got to where you are, @jurriaan. Accually, I got half the variable names the same :). The only problem is that every time I run "codedFileList.readRawVarint32();" it outputs a number (regardless of whether there should be some data there) and it only reads one byte. How am I supposed to know which are real varints and which are just part of the data? Here is what I was able to get codedFileList.readRawVarint32() = 95, bytes read = 1 codedFileList.readRawVarint32() = 10, bytes read = 2 codedFileList.readRawVarint32() = 20, bytes read = 3 codedFileList.readRawVarint32() = 05, bytes read = 4 codedFileList.readRawVarint32() = 03, bytes read = 5 codedFileList.readRawVarint32() = 5430, bytes read = 7

That last one looked interesting so I tries decoding the next 5430 bytes as "File" in the protobuf... no results. Right now I'm goint to try experimenting in a hex editor to see if there's a header of some sort that precedes the first varint.

alexhulbert avatar Nov 18 '13 17:11 alexhulbert