[HN Gopher] Tony Hawk's Pro Strcpy
       ___________________________________________________________________
        
       Tony Hawk's Pro Strcpy
        
       Author : ndiddy
       Score  : 329 points
       Date   : 2024-08-07 16:48 UTC (6 hours ago)
        
 (HTM) web link (icode4.coffee)
 (TXT) w3m dump (icode4.coffee)
        
       | jonhohle wrote:
       | This is awesome!
       | 
       | I've been doing some PSX decompiling and there are lots of
       | similar things there as well. Interestingly, something like
       | `memmove` is linked in using an SDK library[0], but `strcpy` is a
       | function provided by the BIOS. Later version of the SDK could
       | patch that out for a library version, but as late as 1997 it
       | hadn't been.
       | 
       | 0 - https://github.com/Xeeynamo/sotn-
       | decomp/blob/master/src/main...
        
         | anthk wrote:
         | I'd love a reimplemeantion in C+SDL2 (and OpenGL 2.1) of the
         | former console games.
         | 
         | Now there are the N64 games being ported to PC with
         | decompilers, I can only hope. Inb4 "there are native PC
         | versions of these, you know"... most recompiled N64 games with
         | the FX's being 'deshaderized' to pure textures (or simpler
         | FX's) can be run in toasters such as cheap netbooks from 2009
         | and nearly anywhere.
         | 
         | They even ported Super Mario 64 to the 3DFX API. I know, the
         | most complex games accesing the N64 framebuffer with complex FX
         | will require OpenGL 3.3 to mimic that microcode; but, as I said
         | before, when the engines run uber-fast on anything post Pentium
         | III, is not something difficult to 'mimic' these in software
         | while the rest it's running GL 2.1 accelerated.
        
           | bitwize wrote:
           | > They even ported Super Mario 64 to the 3DFX API.
           | 
           | That's... not surprising. UltraHLE ran SM64 like a dream, and
           | the HLE bit referred to the fact that the emulator translated
           | 3D calls to the Glide API rather than attempting to emulate
           | the 3D hardware directly.
        
             | anthk wrote:
             | Yeah, I knew about that, so this it's just transcribing
             | instead of translating. But I'd guess SGI machines (IRIX)
             | being OpenGL bound (they invented it) the N64 would map the
             | microcode to GL funcs much better.
        
               | bitwize wrote:
               | Glide was modelled after OpenGL, so I'm guessing the
               | mapping was not that much of a stretch anyway.
        
       | perihelions wrote:
       | - _" If I was lucky it would be strcpy (opposed to something like
       | strncpy)"_
       | 
       | it really ought to have been strncpy, I'm sure Tony Hawk who's
       | lauded for his advocacy of safety gear would prefer to be
       | associated with safer string copying
        
         | kragen wrote:
         | strncpy is definitely not safer; it produces unterminated
         | strings when it hits _n_
         | 
         | basically you should almost never use strncpy; it's
         | specifically for fixed-size fields like this:
         | struct dirent { unsigned short inode; char name[14]; };
         | 
         | and in those cases more often than not the pad byte should be a
         | space rather than a nul
         | 
         | strncpy should never have been added to the standard library
        
           | sidewndr46 wrote:
           | What is the preferred solution here? I usually just use
           | "memset" to zeroize the whole destination string, then tell
           | "strncpy" that my destination is 1 byte shorter than what it
           | really is.
           | 
           | The real issue I've ran into is that "strncpy" assumes the
           | source is null-terminated.
        
             | connicpu wrote:
             | C11 adds `strcpy_s` which takes (dest, destsz, src) and
             | returns an errno_t which will report an error if the src
             | string is longer than destsz, as silent truncation is often
             | not a desirable behavior. It also assigns dest[0]='\0' on
             | error so you don't get an unterminated garbage string.
        
               | david2ndaccount wrote:
               | Only msvc provides strcpy_s and they don't conform to the
               | standard. Other libcs don't provide it. Ignore everything
               | from Annex K and write your own wrappers around memcpy.
               | You should always know the size of your buffers.
        
               | connicpu wrote:
               | Ah that sucks. Guess C is just stuck like this for the
               | long term. Writing your own functions is still the best
               | advice :'(
        
             | david2ndaccount wrote:
             | Use memcpy and do the size check yourself beforehand
             | (taking the appropriate action if it doesn't fit). Avoid
             | any function starting with str except for strlen. Prefer
             | pointer+length instead of relying on nul-terminated
             | strings.
        
             | paulryanrogers wrote:
             | strlcpy?
        
             | saagarjha wrote:
             | memccpy, then use the return value to terminate.
        
             | pjmlp wrote:
             | Use a proper C string library like SDS.
             | 
             | Or move up from the 1970's Bell Labs, adopt C++ with the
             | respective compiler switches to have bounds checking
             | enabled for _operator[]()_.
             | 
             | Better yet, use something else instead of one of those two,
             | pick whatver is your fancy.
        
             | Sesse__ wrote:
             | The sanest solution is, surprisingly, snprintf(dst,
             | sizeof(dst), "%s", src).
        
         | david2ndaccount wrote:
         | The correct thing to do is to use memcpy and to know the size
         | of both the destination buffer and the source buffer. If the
         | source buffer won't fit, then you need to take an application-
         | specific action (is truncation ok? do you have to abort the
         | whole operation? Do you re-alloc the destination buffer? etc.)
         | strncpy almost always does the wrong thing.
        
         | thekevan wrote:
         | I read that he used to drive around and when he saw a
         | skateboarder, he'd yell "do an ollie" and then give them a new
         | helmet.
        
       | nj5rq wrote:
       | Very good article.
        
       | makin wrote:
       | A bit of a shame about the exploit applying to THUG PRO. The mod
       | is played to this day, since the more competitive side of the
       | Tony Hawk franchise has been dead for almost twenty years (with
       | the exception of the THPS1+2 remake, which was but a blip in the
       | scene).
       | 
       | The mod itself is over 10 years old now, and I think the original
       | developers are gone, explaining why no one was interested in
       | fixing it when Ryan reported it. But this means that now the mod
       | is unusable, no one is going to want to risk a full privilege
       | exploit taking over their PC.
       | 
       | Hopefully this article reaches someone who's a bit more
       | interested in patching the mod.
        
         | rlabrecque wrote:
         | I wish I had the time, because it would be fun. Back when I DID
         | have time, I actually got that thug1 source code almost
         | playable on Windows. That source code was only for the console
         | versions, and the code assumed if it was compiling for windows
         | (and not Xbox windows..) it was only for tools, so a lot of
         | pieces worked completely differently.
        
       | auto wrote:
       | I've read so many flavors of this sort of exploit analysis over
       | the years, and if I get to read 100 more I'll be all the happier
       | for it.
       | 
       | Great article!
        
       | Retr0id wrote:
       | > The more interesting thing about the habibi key is that the
       | public key modulus only has a 4 byte difference compared to the
       | Microsoft RSA public key. For reference the MS key is a 2048 bit
       | RSA key. I've asked a few people how this might be possible and
       | the answer I got is "if you change the exponent to something
       | small like 3 you easily factor out a similar key". This should
       | require that the exponent of the public key is also patched to
       | "3". However, none of the shell code payloads that use the habibi
       | key ever change the exponent used by the RSA signature
       | verification routine. Presumably it's still performing the
       | validation using the exponent 65537 so I'm not entirely sure how
       | this works. Perhaps someone more knowledgeable could shed some
       | light on it.
       | 
       | A random 2048-bit integer has a moderate chance of being
       | trivially factorizeable (I don't know the precise odds but we can
       | infer that it's roughly on the order of 2^-32 (for some
       | definition of trivial) without doing any real math). Presumably,
       | they wrote code that did something like this:
       | while true:             randomly tweak/increment 4 bytes of the
       | public modulus              spend 1 millisecond trying to factor
       | it             did it work? if yes, we're done here.
       | else, try again.
       | 
       | The resulting public modulus likely has lots of smaller factors
       | (it should be possible to verify this, if anyone knows where I
       | can find the "habibi public key"?). Although an RSA modulus
       | normally has exactly 2 prime factors, the math still works out if
       | you have more (as long as e is coprime).
        
         | fxtentacle wrote:
         | Let me try to explain that. You start with a random 2048-bit
         | integer. You then change the lower bytes to make it divisible
         | by 3. This is easy because you're only working on the public
         | key. Now that the public key is divisible by 3, you use
         | Fermat's little theorem which tells you that the private key
         | must be divisible by 3 and have a sum of digits that is
         | divisible by 3. This lets you skip most possible private keys,
         | thereby reducing the compute needed to factorize it by a few
         | orders of magnitude. And maybe you get lucky and they use that
         | RSA implementation which uses exactly 2 prime factors, because
         | then you already know that one of them is 3 and you just divide
         | the public key by 3 to get the other prime factor.
         | 
         | EDIT: Wikipedia says "The structure of the RSA public key
         | requires that N be a large semiprime (i.e., a product of two
         | large prime numbers), that 2 < e < N, that e be coprime to
         | ph(N), and that 0 <= C < N." and later "the same algorithm
         | allows anyone who factors N to obtain the private key."
         | 
         | which in the contest of the Xbox hack means that if you force N
         | to be divisible by the prime 3, then the other prime which is
         | used for generating the private key has to be N/3 => You have
         | successfully factored it.
         | 
         | EDIT2: Here's code for signing with the Habibi key:
         | https://github.com/XboxDev/xbedump/blob/b8cd5cd0f8b1cbc4e64f...
         | 
         | As you can see, it'll replace the last 4 bytes with 0x89, 0x9c,
         | 0x90, 0x6b and then start by dividing it by 3 and using that to
         | generate a suitable private key.
        
           | Retr0id wrote:
           | Ah, thanks for finding that code.
           | 
           | Here's the original public modulus as an integer: http://fact
           | ordb.com/index.php?query=207401193272587237602760... (which
           | can't be factored, at least not any time soon)
           | 
           | And here's the patched version: http://factordb.com/index.php
           | ?query=173718524353649322341982...
           | 
           | And exactly as you say, it's divisible by 3, leaving behind a
           | single large prime (so I was wrong about there being more
           | factors)
        
       | brcmthrowaway wrote:
       | This gives me an opportunity to clarify a myth from my childhood.
       | Was Tony Hawk the first ever to hit a 720?
        
         | zimpenfish wrote:
         | https://en.wikipedia.org/wiki/Aerial_(skateboarding) says "The
         | 720, two full mid-air rotations, is one of the rarest tricks in
         | skateboarding. It was first done by Tony Hawk in 1985, and it
         | wasn't something he planned to do."
         | 
         | (Which is presumably "the first recorded" but I'm guessing if
         | someone had done it, they'd have been shouting about it and
         | -probably- the only kind of person who could pull it off would
         | be a pro skater anyway?)
        
           | detoured299 wrote:
           | At that time only a few pro vert skaters would have had the
           | ability to throw 720s, yeah. Nowadays a good number of ams
           | can too.
           | 
           | The rarity of seeing a 720 or above has as much to do with
           | the fact that most skaters don't skate vert - instead skating
           | street or smaller transition - as the trick's difficulty.
           | Outsiders tend to imagine large spins are the holy grail of
           | skate moves but almost all skaters aren't interested in them
           | for aesthetic reasons among others.
        
           | voytec wrote:
           | He worked much longer for the 900 but more importantly - he
           | repeated the 900 at the age of 48[0]!
           | 
           | [0] https://youtu.be/TnvPt_a7iOQ?t=93
        
       | ComputerGuru wrote:
       | FYI, what looks like a section header icon followed by the text
       | "So what's the habibi key?" is actually a clickable expanding
       | segment (html details). You should click it if you're interested!
       | 
       | A question I have is where/when/how the corresponding _private_
       | habibi key was released /leaked, if the story about it being used
       | exclusively by the linux console group to prevent pirated content
       | from being used is true. OP clearly was able to patch the four
       | byte difference between the MS key and the habibi key to then run
       | "unsigned" (but, actually, signed with the habibi private key)
       | executables, so they clearly got their hands on it.
        
       | jdlyga wrote:
       | Imagine a VSCode plugin that made up trick names and gave you a
       | combo points score at the bottom for your continuous keystrokes.
       | Tony Hawk's Pro-grammer
        
         | i_read_news wrote:
         | I think this would be more fun for VIM keybindings, where there
         | is a higher skill level (to get cooler combos of course).
        
       | Rebelgecko wrote:
       | Thanks for sharing, the other articles on this blog are equally
       | fascinating
        
       | Jerrrrrrry wrote:
       | It may not be possible for me to articulate how fucking insane of
       | an accomplishment this is.
       | 
       | Xbox 360...._softmod_.... via the park name on a Tony Hawk game.
       | 
       | 24 segment ROP chain :')
       | 
       | His rightful lamentation for the hypervisor, concise functional
       | write up, and immediate thoughts of an x360 botnet make this the
       | greatest xbox 360 nostalgia gut-punch of all time.
       | 
       | kudos++
        
       | Reason077 wrote:
       | In Tony Hawk's defence, he's a pro skater, not a security
       | analyst. Limited time behind the keyboard in the late 90s/early
       | 2000s grinding on his soon-to-be iconic game series would have
       | been spent making sure 900 McTwists felt really natural, not
       | auditing code for buffer overruns!
        
       | JoshTriplett wrote:
       | This seems like a great example of having the wrong security
       | mindset in console development. "We're the only thing that can
       | write this saved data, so we only have to parse what we wrote" is
       | a very common console mindset, and fundamentally wrong when
       | people can prepare artificially constructed saved data.
       | 
       | (Completely separate from that, consoles shouldn't be treating
       | users as the adversary, but given that they _do_ , games are
       | failing to have a security mindset consistent with that stance.)
        
       ___________________________________________________________________
       (page generated 2024-08-07 23:00 UTC)