jurriaan / Ruby-iCloud

A Ruby utility for connecting to Apple iCloud services
MIT License
20 stars 7 forks source link

backups #1

Open tcurdt opened 10 years ago

tcurdt commented 10 years ago

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
alexhulbert commented 10 years ago

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?

jurriaan commented 10 years ago

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 notifications@github.com 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.

tcurdt commented 10 years ago

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

alexhulbert commented 10 years ago

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

tcurdt commented 10 years ago

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.

alexhulbert commented 10 years ago

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 commented 10 years ago

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.

tcurdt commented 10 years ago

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 :)

alexhulbert commented 10 years ago

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

tcurdt commented 10 years ago

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

tcurdt commented 10 years ago

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 commented 10 years ago

As the header suggests the call should return a protobuffer.

jurriaan commented 10 years ago

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 ;)

alexhulbert commented 10 years ago

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 commented 10 years ago

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.

jurriaan commented 10 years ago

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 ;)

tcurdt commented 10 years ago

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

jurriaan commented 10 years ago

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 ;)

alexhulbert commented 10 years ago

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

alexhulbert commented 10 years ago

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 commented 10 years ago

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...

jurriaan commented 10 years ago

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 ;)

alexhulbert commented 10 years ago

Oops forgot to push that...

alexhulbert commented 10 years ago

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

jurriaan commented 10 years ago

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

alexhulbert commented 10 years ago

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 commented 10 years ago

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;
jurriaan commented 10 years ago

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));
tcurdt commented 10 years ago

Wow - good progress here!

@jurriaan the jdeb reference was aimed at me ;)

alexhulbert commented 10 years ago

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.

jurriaan commented 10 years ago

If you just assume that the first varint you read is correct, read the number of bytes and assume that the next one is a varint again it should work. That works perfectly here..

Btw, how did you get the Elcomsoft program running over a proxy? Because it does not accept the fake certificates..

alexhulbert commented 10 years ago

I didn't need the content, I just set it up so that it got the headers, etc. and replicated the requests. All the missing info (Path, etc.) was provided by the guy who created in his presentation at some convention. He literally gave away all that extra info I needed to reverse engineer the program. Next time you make a $200 dollar program, don't publically explain all the inner workings of it :)

Also, I was able to get the varint thing working by tweaking the proto file (adding some stuff as optional, and changing some "required"s to "optional")

EDIT: Check this out: https://archive.org/details/Apple_iCloud_Services_reversed_inside_out This would have been helpful a week ago

jurriaan commented 10 years ago

The problem is, he didn’t gave it all away.. only basic things like URLs.. Without information about what to POST to getFiles we’ll get nowhere ;) I think you need to POST the chunks you want to getFiles so it returns some information.. But how..

So if you know a way to extract that information, I’ll be happy to hear it. :)

alexhulbert commented 10 years ago

Accually, I can pick up the POST info just fine! It looks like these are the headers for p16-content.icloud.com/<DsPrsID>/authorizeGet:

x-apple-mmcs-auth - <Base64> <More Base64> x-apple-mmcs-dataclass - com.apple.Dataclass.Backup x-apple-mmcs-proto-version - 3.3 x-apple-mme-dsid - <dsPrsID> x-apple-request-uuid: ????????-????-????-????-???????????? x-mme-client-info: <iPhone OS;5.0.1;9A405> <com.apple.icloud.content/193.4 (com.apple.MobileBackup/9A405)>

The only thing I'm worried about for this it x-apple-request-uuid, but thats probably listed somewhere in the listFiles protobuf, there are a lot of unknown values encoded in hex.

Now all thats left is figuring out how do decrypt stuff using the keys file and "Getting file authentication tokens," however that works

jurriaan commented 10 years ago

I would be very happy if you could tell me how you setup the proxy. Because Elcomsoft Password Breaker refuses to work with a MITM proxy(decrypting https traffic) on.. What did you do to make it work? Is it a specific version?

tcurdt commented 10 years ago

I think the trick would be to restore an iPhone and to listen in on all the traffic. But I don't have spare phone to try :)

jurriaan commented 10 years ago

The problem is that you need to jailbreak the iPhone to be able to do a MITM attack :)

On 18 nov. 2013, at 19:47, Torsten Curdt notifications@github.com wrote:

I think the trick would be to restore an iPhone and to listen in on all the traffic. But I don't have spare phone to try :)

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

tcurdt commented 10 years ago

I don't think you do. You just need install a proper cert. That's how I listen to the https traffic here. Not sure if that's different on the restore.

jurriaan commented 10 years ago

You have to reset your phone before restoring, there is no way to install a cert before restoring without a jailbreak ;)

On 18 Nov 2013, at 20:06, Torsten Curdt notifications@github.com wrote:

I don't think you do. You just need install a proper cert. That's how I listen to the https traffic here. Not sure if that's different on the restore.

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

alexhulbert commented 10 years ago

@jurriaan I don't use anything special, just a program called "fiddler" along with mitmproxy. They both have their strengths and weaknesses, so its nice to have both. I don't actually decrypt the traffic. Its all done through an HTTP tunnel, so the headers are transmitted through HTTP and readable as plain text. The only catch is that you have to figure out the URL's path yourself. Once thats done, its as simple as recreating the request right from fiddler and looking at the data from there. Hope that helps.

All my iOS devices are jailbroken if thats of any use to you. Do I just add the certificate and connect to the proxy? Or is there more involved in the whole process? UPDATE: You can use a provisioning profile for it without a jailbreak! (See this)

EDIT: I just realized something... "x-apple-request-uuid" is present in all the other requests. I already have it defined facepalm

EDIT 2: As for the base64 stuff, I'm not sure how that works. Nothing seems to contain the dsid or mmeAuthToken, so that might be whats located within the protobuf

jurriaan commented 10 years ago

Yeah, but the thing is, the body is sent encrypted.. So, with only a HTTP proxy you are not gonna get there..

The only way would be to add the certificate like described in https://archive.org/details/Apple_iCloud_Services_reversed_inside_out. They add the certificate to the keychain, backup the keychain, reset the iPhone, restore the keychain, setup the proxy, and run a restore.. That way apple trusts the root certificate and does a restore while you can use mitmproxy to read the encrypted data. Or.. You need to find a way to fool Elcomsoft to accept the fake certificate..

alexhulbert commented 10 years ago

I found a way for them to accept it! Just spoof https://setup.icloud.com/setup/qualify/cert?ver=P1.10.1 using the proxy!

alexhulbert commented 10 years ago

Elcomsoft uses that url for getting the certificate

EDIT: Bingo! image

jurriaan commented 10 years ago

How do you want to spoof it without elcomsoft accepting the current certificate? It is not going to connect unless it trusts the SSL certificate

alexhulbert commented 10 years ago

That was my iPod connecting. I noticed elcommsoft connects to the same url. That file contains the certificate im guessing it uses when connecting to apple

GautamAgrawal commented 10 years ago

Hi I am doing the same thing. But I am stuck on getting backup ids. I am getting some hex values or encrypted so I am unable to do further communication to iCloud. Can anybody help in this?

tcurdt commented 10 years ago

@GautamAgrawal re-read through this issue and look at the source code

alexhulbert commented 10 years ago

That's the same problem I had. What language are you writing it in? If its Java/Groovy, you could help with my project. I'm about 1/3 done

GautamAgrawal commented 10 years ago

@Triforce1 am using C++. Are you able to do further communication to iCloud?

alexhulbert commented 10 years ago

Well, I can't help you with C++, but I can tell you that this is done using protobufs What you need to do is use protoc --cpp_out=./ protobuf.proto after dragging the .proto file from my repo (Triforce1/iCEW1ND) to the same directory as protoc. A C++ file should appear. Add it to your code and use it to parse that hex.