First things first, I haven’t done this because I wanted to break or break in to something. I’ve done this because we had the need to do something that the built in stuff wouldn’t allow us to. Also, all the conclusions are just what I think they are, don’t take my word for it.
This is not by any means supported by MS.
I know that by sharing this I probably will shoot myself in the foot. But again, if we need it, more organizations probably do as well, and that’s what making this all worth sharing.
The best scenario would be if there was a native way to handle stuff like:
802.1x:
1. Changing VLAN and IP after a PXE-boot. PXE is only possible on the fallback network. (unauthenticated) If having multiple domains and VLANs, the package source might be unreachable when the client switch VLAN\IP in order to join, e.g., a “Students Only”-domain. (Package sources are created accordingly to the IP the client has when starting a sequence) Don’t think I will have to explain why we don’t want student computers to be able to connect to resources in our main domain
2. MP is still the same after changing VLAN during an OSD. We’re not getting the status messages if we don’t use a “hack” to change the MP after the switch.
Annoying stuff:
1. Software Center performance is being affected by the OSD’s that are available.
(Might take up to a minute to update the view because SC needs to evaluate what to show) I had a workaround, deploying a TS with its available time set into the future, changing it and triggering it by another TS… Doesn’t work since the policies for the programs are downloaded and stored in the TSEnv nomatter what the local WMI says. (As it should be, but it still prevents us from applying a work-around until there’s a fix for it) Edit 2020-01-26
Might have been a bit too harsh on MS. Guess the lack of sleep finally took its toll. MS didn't force me into doing this nor did they take any part in conceiving any of my three children. :P However, I still think that admins should be allowed to be admins and be able to learn from their mistakes rather than locking things down Apple-style.
Other stuff (95%):
1. There’s no way any company will know what another company needs for sure. Call it telemetry, “we will take care of it for you” or anything.
Help us choosing what might be best for us, sure. But saying “We know what you need, we got the data” and locking anything else out, that’s just not right.
Probably works kind of good for large enterprises, but count on the number of companies with less than 1000 employees and the unique needs for every one of them.
Rant of, back to the original thought of this post. =P
How does the structure of the TSEnv look like? I will refer to the data of a variable as a variable block.
So what does a variable block consist of? Let’s take _SMSTSMDataPath as an example.
The purple is obviously the name. This is the part which TSEnv2 and I renamed in order to make a “_”-variable writable.
Edit 2019-12-22: That trick doesn't work on the Base64 type of variables. At least I havn't found a way.
I was still relying on the TSEnv Comobject to set the value after renaming the variable.
The Comobject seems to add 's' as a padding char while the Base64 type needs to be padded with a 'b' .
Else you'll end up with an error when the TS tries to use it, at least during and OSD, in full OS, when getting to a "install package"-step.
That's the main reason of why I gave this another go.
The yellow part contains 3 clusters holding length data.
2F 00 00 00 - The length from the next cluster to the next variable block
15 00 00 00 - The length of the unencrypted data.
20 00 00 00 - The length of the encrypted data.
The green part is the actual encrypted data and the red just random junk.
The Yellow
Let’s start with the unencrypted length part, 0x15 (21).
The variable value is the default “C:\_SMSTaskSequence”.
But hey, the length of “C:\_SMSTaskSequence” is only 19, right? Yeah, it is, but SCCM and the comobject adds a padding char in the beginning and a 0x00 at the end.
To demonstrate, look at the hex values marked as green in the picture above and compare.
So what about 0x2F (47)? This one I’m not 100% sure of, I mean, I know how to calculate what it should be, but not the reason of why. In all my tries, no matter if the unencrypted length is 2 (the absolute minimum with the padding) or 4334, this value has always been the unencrypted length + 26. So in the e.g. 21+26=47. It is also the number of bytes from the 2nd cluster (including) 0x15 to the next variable block.
And 0x20 (32)? This is used to separate the green part from the red. In other words how many bytes of data there is to decrypt.
Oh, and btw, there’s another cluster at the very start of the tsenv memory. This is also a length block and it points out the length from the 5F to the last byte of the last variable block. So any update or change of the data in-between also requires you to update this cluster to reflect the changes made.
Making functions that handles all these calculations while changing a variable value is what has been taking most of the time I spent on this matter. Probably because I suck at hex but still. =P
The Green
The encrypted bytes of the (padded) data.
Two of these registry keys contains vital information. The names of the memory mapped sections and a “random” generated hex string for each of them.
I’m mostly interested in “\BaseNamedObjects\{51A016B6-F0DE-4752-B97C-54E6F386A912}” since that contains the TSEnviroment variables.
Reserved1 is the password for the encryption but it’s not as easy as typing it and that’ll give you full access. When monitoring the API while setting a variable using the TSEnv comobject I’ve seen that it calls bcrypt and some RSA function with AES256 and IV as parameters but the key parameter didn’t in any way look like the reg value.
So how is that reg value related to decrypt something using a function which seems to require a certificate? That’s the million dollar question.
First thing that came in mind was that the AES key might be a hash derived from the reg value. But it turned out that the reg value was too short to be used as an AES key… Eventually I finally found the answer…
But that is for another post and when the tool for it is Alpha ready.
PS. So what’s the deal, it returns the same info as the comobject? Well, ever wondered why all the base64 variables in the TSEnv starts with the same chars? It’s because they are compressed and the first bytes contains compression header data.
If you could decompress it, edit it, recompress it and then rewrite it to the TSEnv again… What would that allow you to do?
Comments