░░░                              ░░░             ▀▄
                       ▄ ▀▀▄              ░░░                                   ■  ▒          ░░░
                     ▄      ▒        ▄▄                ▒    ▄▀       ▒         ▄  ▀▄▄
                     ▄▄             ▄                ▄█    █       ▄▀           ▄█▀▀▀     ▀▀ ▄
                      ▀▄▀█▄▄▄▄▄▄██▀ ▀▄▀███▄▄▄      ▄▄█▄▄▄ █▄▄▄   ▄▀▄▄▀  ▄▄▄▄▄    ▀▄▀ ▒    ▄▄▄▄▀
                      ▀▀▄▄  █▄▀▀▄     ▄▀ ▄▄ ▀▄   ▄▀▄▀▀▀▀▄  ▀▄▀▀▄██▀█   █▀▌  ▐▄          ▄█▀▀▀▀▄▄
                            █▀ ▄▄ ▒   ░░   █ █▀ ▄▄▀ ■▀ ▀▄  ▒ ▀▄▀▄▀ █  ▀▄    ██▄  ▓     ▄█ ▄▀▀ ██▀▄
           ▄              ▒ ██   ▄    ▓▓   ▒ █▄ ██ █   ██  █▄  ▀  ▀▒  ██   ▐▌██  █ ▒   ▄█ ▓   ██            ▄
             ▀            ▒ ▀█     ▄  ██     █  ░░ ▒   ▓▓  █▄     ██  ▓▓   █ ░█  ▄ ▄   ██ ▒   ▀▄          ▀
            ▀▄            ▓ ▓█      ▀▀█▀▄▄▄▄▀   ▒▒     ░░▀ ▒▒ ░   ▒▒  ░░  ▐▌ ██  █ ▒   ▀▀ ▄▄▄▄▄ ▄         ▄▀
           ░█▀            █ █▓      ▒ ▓ ▀▀▄    ▄▄▄▄▄▓▓▀▄█  ░▓ ▒   ▓░▐ █▓  ▓  ▌▓  ░▒░ ▄▀██  ▄▄▄▀▀          ▀█░
           ▄╬▄            ▓ ▓▒      █ █▓   ▀▒▄  ▀░▀▀   █▓  ▓█ █   █▓  ▓█ ▐▌  ▓▓  ▓▓█ ▄▀█▓     ▄█ ▒        ▄╬▄
      ,    ▌█▌    ,       ▓ ░▀      ░ ▓█  ▀  ▀░░▄▄     ░▓  █▓ █   ▒█▐ ▌▓ █   ▌▒  █▓█   ░█ ▒   █▓ █   ,    ▐█▐    ,
      ▄ ▄ ═▌█▌═ ▄ ▄  ░░▀▀▀▀ ██      ░ █▒    ▀▄ ▀▓░░░▄▄ █░  ▓░ ▓   ▀█▐ █░▐▌   ▌█  ░░░   ▀░ ▓   ░█ ▀   ▄ ▄ ═▐█▐═ ▄ ▄ 
       ▀▀█▓▄▓█▓█▀▀    ▀▄ ▀░░░░   ▒  ▀▄▓▀▒     █  █▓▄▀▀▀░▄▀░░█ █   ░░█▀▀▓█    █   ░▓█ ▀▄▓█ ▓   ░  ▄▄   ▀▀█▓█▓▄▓█▀▀  
           ▀▓▄%         ▀  ▀▄     ▀░░▀ ▄    ▄▀  ▄▀     █▀  ▀█ ▓   ░▀▄▄  ▀ ▄▄▀   ▒▀ █▄  ▀█ █   ▓▄▀  ▀     %▄▓▀      
   ▒      •▀▒             ▀▄        ▄▄▀    ▒  ▄▀          ▀▄  ▓   ▄▀   ▀ ▄▄  ▀    ▄▀  ■▄  ░   ▀            ▒▀•      ▒   
 ▄▀▀▀▀▄▄▄█▄▄▒                ■                ░     ▒        ▀█         ■       ▄▀      ▀▄░                ▒▄▄█▄▄▄▀▀▀▀▄ 
 ░ ░  •▀                                       ▀ ▄▄▀                           ▒          ▀                     ▀•  ░ ░ 
 █ ▒▄▀                                                                               [ Arte por L.Ayres ]         ▀▄▒ █ 
 █ ▒                                                                                                                ▒ █
 █ █                                                                                                                █ █
 █ ▒                                          o                                                                     ▒ █
 █▒▒                                ___      o                                                                      ▒▒█
 █ ▒                               ( - -\__»»                                                                       ▒ █
 █ █                   ___________.'/'--'                                                                           █ █
 █ ▒               ___( ___________|_____                                                                           ▒ █
 █▒▒            __|___|___________ )_____|                                                                          ▒▒█
 █ ▒           <   |  |___________ )    ||                                                                          ▒ █
 █ █            \  |  |___________ )    ||                                                                          █ █
 █ ▒             \ |__|___________ )____|`------.                                                                   ▒ █
 █▒▒              \|__|___________ )____| _____ |                                                                   ▒▒█
 █ ▒               |  |___________ )     |     ||                                                                   ▒ █
 █ █               |  |___________ )     |     ||                                                                   █ █
 █▒▒ ¡             |  |___________ )     |     ||                                                                   ▒▒█
 █ ▒,█▄            |  ( ___________|     |     ||                                                                   ▒ █
 █ █▀▀°            |  |___________ )     |    //'                                                                   █ █
 █▒▒               |   ___________ |     |  //'                                                                     ▒▒█
 █ ▒               (  ( ___________)     )//'                                                                       ▒ █
 █ █               ;  +-----------.`\    ;'                                                                         █ █
 █ ▒                \     <   ^   >`\Oo /                                                                           ▒ █
 █▒▒                 `\    `\ | /'    /'                                                                            ▒▒█
 █ ▒                   `\____|||____/'                                                                              ▒ █
 █ █                    |    ___    |                                                                               █ █
 █ ▒                   /    (___)    \                                                                              ▒ █
 █▒▒                  |_______________|                                                                             ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █                                                                                                                █ █
 █ ▒                                                                                                                ▒ █
 █▒▒          Imagens sao dificeis - Thumbor 0days                                                                ¡ ▒▒█
 █ ▒          por caioluders                                                                                     ▄█,▒ █
 █ █                                                                                                             °▀▀█ █
 █▒▒          1 - Introducao: Por que Thumbor?                                                                      ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          Eu queria estudar pwning, então procurei por algum projeto open-source "grande",                      █ █
 █ ▒          brasileiro e com essa superfície de ataque. Pedi pra IA procurar e me dar umas                        ▒ █
 █▒▒          opções e o thumbor pareceu ser legal pra fazer pesquisa, sei lá.                                      ▒▒█
 █ ▒          Essa pesquisa é mais didática que ofensiva e os exploits apresentados são bem                         ▒ █
 █ █          difíceis de explorar no mundo real. De toda forma, os bugs são interessantes e                        █ █
 █ ▒          me fizeram aprender bastante, espero que a vocês também.                                              ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          O Thumbor é um serviço open-source de processamento de imagens criado                                 ▒ █
 █ █          pela Globo.com [10] [11]. É amplamente usado em produção para resize, crop e                          █ █
 █ ▒          aplicacao de filtros on-the-fly. 10k+ estrelas.                                                       ▒ █
 █▒▒ ¡                                                                                                              ▒▒█
 █ ▒,█▄       Basicamente a API exposta pra internet consiste da url da imagem que será                             ▒ █
 █ █▀▀°       processada, os filtros que serão aplicados , seus parâmetros e do tamanho final                       █ █
 █▒▒          da imagem (também existe uma assinatura por segurança que explicarei ja ja).                          ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          +-------------------------------------------------------------------+                                 █ █
 █ ▒          |                         REQUISIÇÃO HTTP                           |                                 ▒ █
 █▒▒          |                                                                   |                                 ▒▒█
 █ ▒          |   /unsafe/{largura}x{altura}/filters:{filtro}({params})/url       |                                 ▒ █
 █ █          +-------------------------------------------------------------------+                                 █ █
 █ ▒                                         |                                                                      ▒ █
 █▒▒                                         v                                                                      ▒▒█
 █ ▒          +-------------------------------------------------------------------+                                 ▒ █
 █ █          |                         SERVIDOR THUMBOR                          |                                 █ █
 █ ▒          |                                                                   |                                 ▒ █
 █▒▒          |   +-----------+      +-----------+      +----------------------+  |                               ¡ ▒▒█
 █ ▒          |   |  Loader   |----->|    PIL    |----->|  Filtros em C        |  |                              ▄█,▒ █
 █ █          |   | (http/fs) |      | (decode)  |      | (_composite,         |  |                              °▀▀█ █
 █▒▒          |   +-----------+      +-----------+      |  _sharpen,           |  |                                 ▒▒█
 █ ▒          |        |                   |            |  _nine_patch, ...)   |  |                                 ▒ █
 █ █          |        |                   |            +----------------------+  |                                 █ █
 █ ▒          |   entrada do usuário    parsing             código nativo         |                                 ▒ █
 █▒▒          +-------------------------------------------------------------------+                                 ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █                                                                                                                █ █
 █ ▒          Alguns filtros foram implementados em C como extensões para o Python, aqui que                        ▒ █
 █▒▒          fica legal.                                                                                           ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          2 - HMAC vs .replace()                                                                                █ █
 █ ▒                                                                                                                ▒ █
 █▒▒ ¡            # URL segura vs insegura                                                                          ▒▒█
 █ ▒,█▄           /unsafe/300x200/https://exemplo/image.jpg               # Sem verificacao                         ▒ █
 █ █▀▀°           /8Lz2x5...HMAC/300x200/https://exemplo/image.jpg        # HMAC signature                          █ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          O Thumbor usa HMAC-SHA1 para assinar URLs e prevenir abuso, como carregar                             █ █
 █ ▒          imagens arbitrárias de qualquer URL ou chamar qualquer filtro, sem isso daria                         ▒ █
 █▒▒          pra colocar qualquer imagem no subdomínio do g1 por exemplo. Todo thumbor em                          ▒▒█
 █ ▒          prod deveria estar configurado assim.                                                                 ▒ █
 █ █                                                                                                                █ █
 █ ▒          Então para um ataque completamente externo dar certo você precisa bypassar o                          ▒ █
 █▒▒          HMAC-SHA1, apesar do SHA1 ter fraquezas conhecidas ha quase 20 anos e com                             ▒▒█
 █ ▒          varios ataques de colisao comprovados [3]. Infelizmente nenhum deles ajuda aqui:                      ▒ █
 █ █          a seguranca do HMAC depende da funcao de compressao ser uma PRF, nao de                               █ █
 █ ▒          resistencia a colisoes [1]. O Shambles necessita que voce saiba o prefixo                             ▒ █
 █▒▒          que gerou a hash, que seria justamente o segredo do HMAC [2], entao podemos                         ¡ ▒▒█
 █ ▒          descartar colisoes (mas nao usem SHA1 de todo jeito, nao custa nada usar                           ▄█,▒ █
 █ █          SHA256).                                                                                           °▀▀█ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          Estado atual de colisões SHA1:                                                                        ▒ █
 █ █                                                                                                                █ █
 █ ▒          Custo aproximado (USD)                                                                                ▒ █
 █▒▒          ^                                                                                                     ▒▒█
 █ ▒          |  ∞   |        █  2005 — Ataque Teórico (primeiras colisões)                                         ▒ █
 █ █          |      |        █  https://link.springer.com/chapter/10.1007/11535218_2                               █ █
 █ ▒          |      |                                                                                              ▒ █
 █▒▒          |120k  |                    █  2017 — SHAttered                                                       ▒▒█
 █ ▒          |      |                    █  https://eprint.iacr.org/2017/190                                       ▒ █
 █ █          |      |                                                                                              █ █
 █ ▒          | 60k  |                                                                                              ▒ █
 █▒▒ ¡        |      |                                                                                              ▒▒█
 █ ▒,█▄       | 20k  |                           █  2020 — SHA-1 is a Shambles                                      ▒ █
 █ █▀▀°       |      |                           █  https://eprint.iacr.org/2020/014                                █ █
 █▒▒          |      |                                                                                              ▒▒█
 █ ▒          |  0k  |                                                                                              ▒ █
 █ █          +-------------------------------------------------------------------------> Ano                       █ █
 █ ▒                             2005        2017   2020                                                            ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          Ja que precisamos conseguir carregar imagens arbitrarias no servidor, e para                          ▒ █
 █ █          isso precisamos bypassar a checagem do HMAC vamos procurar por bugs logicos.                          █ █
 █ ▒                                                                                                                ▒ █
 █▒▒          thumbor/thumbor/handlers/imaging.py                                                                   ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █                                                                                                                █ █
 █ ▒                  url_signature = self.context.request.hash                                                     ▒ █
 █▒▒                  if url_signature:                                                                           ¡ ▒▒█
 █ ▒                      signer = self.context.modules.url_signer(                                              ▄█,▒ █
 █ █                          self.context.server.security_key                                                   °▀▀█ █
 █▒▒                      )                                                                                         ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █                      try:                                                                                      █ █
 █ ▒                          quoted_hash = quote(self.context.request.hash)                                        ▒ █
 █▒▒                      except KeyError:                                                                          ▒▒█
 █ ▒                          self._error(400, f"Invalid hash: {self.context.request.hash}")                        ▒ █
 █ █                          return                                                                                █ █
 █ ▒                                                                                                                ▒ █
 █▒▒                      url_to_validate = url.replace(                                                            ▒▒█
 █ ▒                          f"/{self.context.request.hash}/", ""                                                  ▒ █
 █ █                      ).replace(f"/{quoted_hash}/", "") # <-------- REPLACE                                     █ █
 █ ▒                                                                                                                ▒ █
 █▒▒ ¡                    valid = signer.validate(                                                                  ▒▒█
 █ ▒,█▄                       unquote(url_signature).encode(), url_to_validate                                      ▒ █
 █ █▀▀°                   )                                                                                         █ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          Como a hash esta contida na propia URL que ela precisa calcular, o Thumbor faz                        █ █
 █ ▒          um .replace() para remover a hash da URL, assim como a hash com URL-encoding                          ▒ █
 █▒▒          (pois o SHA1 resulta em = %3d no final). O problema eh que o .replace()                               ▒▒█
 █ ▒          remove tudo na URL que corresponder, nao apenas o primeiro que encontrar. Isso                        ▒ █
 █ █          quer dizer que a gente consegue inserir a hash varias vezes em qualquer lugar                         █ █
 █ ▒          da URL e o Thumbor vai validar (: Ah mas e dai?                                                       ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          http://localhost:8888/ddoyYVYUbDf6Po_dzOrBhCDrXLc=/300x200/s3.glbimg.com/v1/AUTH_bc8228b6673          ▒ █
 █ █          f488aa253bbcb03c80ec5/audiopub-episodes/bs/2025/j/7/6At7gYSBADbccmAiYtAg/agif25082417424226.          █ █
 █ ▒          jpg                                                                                                   ▒ █
 █▒▒                                                                                                              ¡ ▒▒█
 █ ▒                                                                                                             ▄█,▒ █
 █ █          Aqui temos uma linda imagem com hash ddoyYVYUbDf6Po_dzOrBhCDrXLc= e que                            °▀▀█ █
 █▒▒          carrega diretamente de s3.glbimg.com.                                                                 ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          http://localhost:8888/ddoyYVYUbDf6Po_dzOrBhCDrXLc=/300x200/s3.glbimg.co/ddoyYVYUbDf6Po_dzOrB          █ █
 █ ▒          hCDrXLc=/m/v1/AUTH_bc8228b6673f488aa253bbcb03c80ec5/audiopub-episodes/bs/2025/j/7/6At7gYSBAD          ▒ █
 █▒▒          bccmAiYtAg/agif25082417424226.jpg                                                                     ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █                                                                                                                █ █
 █ ▒          Agora a imagem carregada vem de s3.glbimg.co ! Note que inserimos a hash                              ▒ █
 █▒▒          depois do .co , resultando em outro dominio.                                                          ▒▒█
 █ ▒          Mais bypass? O Thumbor faz o replace duas vezes, permitindo que manipulemos                           ▒ █
 █ █          com mais finesse. Se colocarmos um quoted_hash no meio de uma hash normal                             █ █
 █ ▒          dividida no meio, da pra criar paths sem o = , que facilita a exploracao.                             ▒ █
 █▒▒ ¡                                                                                                              ▒▒█
 █ ▒,█▄       http://localhost:8888/ddoyYVYUbDf6Po_dzOrBhCDrXLc=/300x200/s3.glbimg.com/ddoyYVYUbDf6Po/ddoy          ▒ █
 █ █▀▀°       YVYUbDf6Po_dzOrBhCDrXLc%3D/ddoyYVYUbDf6Po/v1/AUTH_bc8228b6673f488aa253bbcb03c80ec5/audiopub-          █ █
 █▒▒          episodes/bs/2025/j/7/6At7gYSBADbccmAiYtAg/agif25082417424226.jpg                                      ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █                                                                                                                █ █
 █ ▒          MAIS BYPASS?                                                                                          ▒ █
 █▒▒          O Thumbor usa uma configuracao ALLOWED_SOURCES para garantir que as imagens                           ▒▒█
 █ ▒          so serao carregadas de um certo dominio, talvez para nao necessitar do HMAC.                          ▒ █
 █ █          O problema ? Eh uma lista de regex e maioria das documentacoes/exemplos nao                           █ █
 █ ▒          escapa os pontos, fazendo o . funcionar como wildcard :>                                              ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          # ALLOWED_SOURCES = ["mydomain.com"]     # "." casa com qualquer char                                 ▒ █
 █ █          ALLOWED_SOURCES=['http://s.glbimg.com']  # aceita sXglbimg.com                                        █ █
 █ ▒                                                                                                                ▒ █
 █▒▒                                                                                                              ¡ ▒▒█
 █ ▒          Permitindo http://sAglbimg.com, ou qualquer outro caractere no lugar do ponto.                     ▄█,▒ █
 █ █                                                                                                             °▀▀█ █
 █▒▒          3 - Quase-Vulnerabilidades                                                                            ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          Ja que superamos o problema do HMAC, vamos procurar bugs de pwn de verdade.                           █ █
 █ ▒                                                                                                                ▒ █
 █▒▒          No principio, tudo era grep. Sai greppando tudo que é função possivelmente                            ▒▒█
 █ ▒          problemática em C. "Clawd please find every unsafe function in this repository".                      ▒ █
 █ █          Uma metodologia claramente ineficiente. Aqui vai uma lista das vulnerabilidades                       █ █
 █ ▒          inúteis, que não da pra alcançar pela API web ou tem alguma pré-condição.                             ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          +--------------+----------------------+------------------------------+                                ▒ █
 █ █          | Filtro       | Bug                  | Código                       |                                █ █
 █ ▒          +--------------+----------------------+------------------------------+                                ▒ █
 █▒▒ ¡        | _alpha.c     | Loop infinito        | mode vem do PIL              |                                ▒▒█
 █ ▒,█▄       |              | se mode vazio        |------------------------------|                                ▒ █
 █ █▀▀°       |              |                      | _alpha.c:18                  |                                █ █
 █▒▒          |              |                      | int num_bytes =              |                                ▒▒█
 █ ▒          |              |                      |   bytes_per_pixel(...);      |                                ▒ █
 █ █          |              |                      | _alpha.c:25                  |                                █ █
 █ ▒          |              |                      | for(; i <= size;             |                                ▒ █
 █▒▒          |              |                      |     i += num_bytes) {        |                                ▒▒█
 █ ▒          +--------------+----------------------+------------------------------+                                ▒ █
 █ █          | _fill.c      | Divisão por zero     | Buffer do HTTP nunca vazio   |                                █ █
 █ ▒          |              | se buffer vazio      |------------------------------|                                ▒ █
 █▒▒          |              |                      | _fill.c:21                   |                                ▒▒█
 █ ▒          |              |                      | int image_area =             |                                ▒ █
 █ █          |              |                      |   (size / num_bytes);        |                                █ █
 █ ▒          |              |                      | _fill.c:31                   |                                ▒ █
 █▒▒          |              |                      | r /= image_area;             |                              ¡ ▒▒█
 █ ▒          +--------------+----------------------+------------------------------+                             ▄█,▒ █
 █ █          | _convolution | Buffer mismatch OOB  | PIL normaliza as dimensões   |                             °▀▀█ █
 █▒▒          | .c           |                      |------------------------------|                                ▒▒█
 █ ▒          |              |                      | _convolution.c:74            |                                ▒ █
 █ █          |              |                      | int width_bytes_count =      |                                █ █
 █ ▒          |              |                      |   width * num_bytes;         |                                ▒ █
 █▒▒          |              |                      | _convolution.c:87            |                                ▒▒█
 █ ▒          |              |                      | int tmp_idx = ...            |                                ▒ █
 █ █          +--------------+----------------------+------------------------------+                                █ █
 █ ▒          | _bounding_bo | Integer overflow     | Não alcançável via HTTP      |                                ▒ █
 █▒▒          | x.c          |                      |------------------------------|                                ▒▒█
 █ ▒          |              |                      | _bounding_box.c:37           |                                ▒ █
 █ █          |              |                      | reference_pixel = image +    |                                █ █
 █ ▒          |              |                      |   (width * stride * ...);    |                                ▒ █
 █▒▒ ¡        +--------------+----------------------+------------------------------+                                ▒▒█
 █ ▒,█▄       | _curve.c     | Truncation uchar     | Heap corruption "cega"       |                                ▒ █
 █ █▀▀°       |              |                      |------------------------------|                                █ █
 █▒▒          |              |                      | _curve.c:9                   |                                ▒▒█
 █ ▒          |              |                      | items[i*2] =                 |                                ▒ █
 █ █          |              |                      |   (unsigned char)PyLong...   |                                █ █
 █ ▒          +--------------+----------------------+------------------------------+                                ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          4 - Meu deus quanto Integer Overflow                                                                  █ █
 █ ▒                                                                                                                ▒ █
 █▒▒          Como a gente viu antes, a imagem passa primeiro pelo Pillow, ela é lida, relida                       ▒▒█
 █ ▒          e parseada pela maior biblioteca de imagem do Python [12], então praticamente todos                   ▒ █
 █ █          os inputs dos filtros em C vem do PIL, que da pra "confiar". Certo? CEEERTOO??                        █ █
 █ ▒          Sim, certo, é a porra do PIL isso aqui não é um 0day que afeta toda a internet,                       ▒ █
 █▒▒          vai com calma. Então a imagem precisa ser válida, nada de tamanho errado,                           ¡ ▒▒█
 █ ▒          formato válido, tudo válido.                                                                       ▄█,▒ █
 █ █                                                                                                             °▀▀█ █
 █▒▒          O problema é que quase todos os filtros usam int32 para lidar com as dimensões                        ▒▒█
 █ ▒          da imagem [9]. int32, que vai de -2.147.483.648 até 2.147.483.647,                                    ▒ █
 █ █          altura = -2 bilhões, comprimento = -100 milhões, é a imagem mais estranha do                          █ █
 █ ▒          mundo.                                                                                                ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          Sabe o que acontece se você mandar uma imagem de largura 2.147.483.64OITO ?                           ▒ █
 █ █                                                                                                                █ █
 █ ▒          int32                                                                                                 ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒           00000000 00000000 00000000 00000000   = 0                                                            ▒ █
 █ █           ...                                                                                                  █ █
 █ ▒           01111111 11111111 11111111 11111111   = 2147483647 (MAX)                                             ▒ █
 █▒▒ ¡                             + 1                                                                              ▒▒█
 █ ▒,█▄                            │                                                                                ▒ █
 █ █▀▀°                            v                                                                                █ █
 █▒▒           10000000 00000000 00000000 00000000   = -2147483648 (OVERFLOW)                                       ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █                                                                                                                █ █
 █ ▒          É a maior imagem em linha reta do mundo.                                                              ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          E sabe o que quase todos os filtros em C fazem com width/height da imagem?                            ▒ █
 █ █          Cálculo de offset de ponteiro, que maravilha. Esse padrão se repete por quase                         █ █
 █ ▒          todos os filtros, uns mais exploráveis que outros.                                                    ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒              // thumbor/ext/filters/_composite.c:45                                                            ▒ █
 █ █              int line_offset1 = ((y_pos + y - start_y) * width1 * num_bytes);                                  █ █
 █ ▒              //                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                       ▒ █
 █▒▒              //                   int32 * int32 * int32 = OVERFLOW!                                          ¡ ▒▒█
 █ ▒              ...                                                                                            ▄█,▒ █
 █ █              aux1 = ptr1 + line_offset1 + (x_pos * num_bytes);                                              °▀▀█ █
 █▒▒              ...                                                                                               ▒▒█
 █ ▒                          r1 = aux1[r_idx];                                                                     ▒ █
 █ █                                                                                                                █ █
 █ ▒                                                                                                                ▒ █
 █▒▒          Se você der um número negativo para um índice, ou subtrair de um ponteiro, dá                         ▒▒█
 █ ▒          pra escrever antes do buffer da nossa imagem na heap (: Bora calcular:                                ▒ █
 █ █                                                                                                                █ █
 █ ▒              width1 = 65500                                                                                    ▒ █
 █▒▒              y_pos = 16393                                                                                     ▒▒█
 █ ▒              num_bytes = 4                                                                                     ▒ █
 █ █                                                                                                                █ █
 █ ▒              Calculo: 16393 * 65500 * 4 = 4294966000                                                           ▒ █
 █▒▒ ¡                                                                                                              ▒▒█
 █ ▒,█▄           Como uint32: 4,294,966,000 (valido)                                                               ▒ █
 █ █▀▀°           Como int32:  -1296 (OVERFLOW!)                                                                    █ █
 █▒▒                                                                                                                ▒▒█
 █ ▒              buffer[-1296] !                                                                                   ▒ █
 █ █                                                                                                                █ █
 █ ▒                                                                                                                ▒ █
 █▒▒          4.1 - Diagrama de Memória                                                                             ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          Ok , a gente consegue escrever antes do nosso buffer, mas o que tem antes do                          █ █
 █ ▒          buffer? Vamos pensar no layout da heap. Ainda estamos falando de modo geral,                          ▒ █
 █▒▒          tanto o controle do ponteiro, quanto o controle do que vai ser escrito tem suas                       ▒▒█
 █ ▒          grandes dificuldades, que serão explicadas mais na frente. O buffer da nossa                          ▒ █
 █ █          imagem é grande devido a necessidade da largura ser grande o suficiente para                          █ █
 █ ▒          causar o integer overflow, na prática isso quer dizer que o malloc (não o                             ▒ █
 █▒▒          pymalloc, que so lida com objetos <= 512 bytes [13]) vai alocar nosso buffer                        ¡ ▒▒█
 █ ▒          via mmap [7] numa página de memória só para ele, ficando longe de qualquer                         ▄█,▒ █
 █ █          outro objeto minimamente útil. Se tentarmos escrever muito longe teremos dois                      °▀▀█ █
 █▒▒          problemas, a imagem vai ficar muito grande (>4gb) e o layout vai ser muito                            ▒▒█
 █ ▒          imprevisível devido ao ASLR. Então efetivamente só conseguimos sobrescrever o                         ▒ █
 █ █          header do nosso própio buffer (pelo menos eu só consegui isso, aceito ideias).                        █ █
 █ ▒                                                                                                                ▒ █
 █▒▒          Nossa imagem é um PyBytesObject [4], que tem os seguintes elementos na sua struct                     ▒▒█
 █ ▒          (struct meramente ilustrativa):                                                                       ▒ █
 █ █                                                                                                                █ █
 █ ▒          typedef struct {                                                                                      ▒ █
 █▒▒              /* ---- PyObject ---- */                                                                          ▒▒█
 █ ▒              Py_ssize_t ob_refcnt;        /* Contador de referencias */                                        ▒ █
 █ █              struct _typeobject *ob_type; /* &PyBytes_Type */                                                  █ █
 █ ▒                                                                                                                ▒ █
 █▒▒ ¡            /* ---- PyVarObject ---- */                                                                       ▒▒█
 █ ▒,█▄           Py_ssize_t ob_size;          /* tamanho do objeto */                                              ▒ █
 █ █▀▀°                                                                                                             █ █
 █▒▒              /* ---- PyBytesObject ---- */                                                                     ▒▒█
 █ ▒              Py_hash_t ob_shash;          /* cached hash (-1 se não computado) */                              ▒ █
 █ █                                                                                                                █ █
 █ ▒              /* flexible array member */                                                                       ▒ █
 █▒▒              char ob_sval[1];             /* começo do buffer (+1 NULL) */                                     ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          } PyBytesObject;                                                                                      █ █
 █ ▒                                                                                                                ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          Desses, o mais interessante é o *ob_type , ele é um ponteiro para um                                  ▒ █
 █ █          PyTypeObject que determina o tipo desse objeto. No nosso caso, o tipo Bytes.                          █ █
 █ ▒          Teoricamente, se construirmos um fake PyTypeObject do jeito certo, conseguimos                        ▒ █
 █▒▒          apontar o tp_dealloc para qualquer função quando ele for dealocado [6], quando                      ¡ ▒▒█
 █ ▒          ob_refcnt chegar a zero, tipo um system(). Existe tambem a possibilidade de                        ▄█,▒ █
 █ █          usar o tp_repr ou tp_getattro com mais consistencia [14] [15].                                     °▀▀█ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          Layout da nossa imagem PyBytesObject:                                                                 ▒ █
 █ █                                                                                                                █ █
 █ ▒                                  <-- OVERFLOW WRITE (-1296) do cálculo anterior                                ▒ █
 █▒▒                                  |   iria pra algum lugar incerto                                              ▒▒█
 █ ▒              +-------------------+------------------------------------+                                        ▒ █
 █ █              |   PyBytesObject   |         Image Buffer               |                                        █ █
 █ ▒              +-------------------+------------------------------------+                                        ▒ █
 █▒▒              | -32: ob_refcnt    |                                    |                                        ▒▒█
 █ ▒              | -24: ob_type  <---+-- ALVO!                            |                                        ▒ █
 █ █              | -16: ob_size      |                                    |                                        █ █
 █ ▒              |  -8: ob_hash      |                                    |                                        ▒ █
 █▒▒ ¡            |   0: ob_sval -----+--> [os pixels/pixeus/pixeis?]      |                                        ▒▒█
 █ ▒,█▄           +-------------------+------------------------------------+                                        ▒ █
 █ █▀▀°                                                                                                             █ █
 █▒▒              Novo cálculo:                                                                                     ▒▒█
 █ ▒                       offset = -1296 + (x_pos * 4)                                                             ▒ █
 █ █                       x=318 -> offset = -24 (ob_type!)                                                         █ █
 █ ▒                                                                                                                ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          Então, se conseguirmos que o offset seja -24 dá pra sobrescrever o ponteiro                           ▒ █
 █ █          ob_type e fazer ele apontar para um objeto fake nosso.                                                █ █
 █ ▒                                                                                                                ▒ █
 █▒▒          Agora, para os (っ◔◡◔)っ ♥ P R O B L E M A S ♥                                                        ▒▒█
 █ ▒                                          tem muito problema jesus.                                             ▒ █
 █ █                                                                                                                █ █
 █ ▒          Primeiro: A gente não tem nenhum infoleak para descobrir os valores dos                               ▒ █
 █▒▒          ponteiros, do ponteiro do system, do ponteiro do nosso PyBytesObject, etc.                          ¡ ▒▒█
 █ ▒          Segundo: Vou explicar com mais detalhes, mas não controlamos 100% do que é                         ▄█,▒ █
 █ █          escrito. Efetivamente os valores escritos são pixeis com seus R G B A (4bytes)                     °▀▀█ █
 █▒▒          Terceiro: Problema menor, mas nossa imagem precisa ter alguns gigas de tamanho.                       ▒▒█
 █ ▒          SE O SEU SERVER TEM MENOS DE 4gb DE MEMORIA O PROBLEMA EH SEU.                                        ▒ █
 █ █          Quarto: sei la porra não roda no mercúrio retrógrado.                                                 █ █
 █ ▒                                                                                                                ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          4.2 - 1o Problema: Blend Math                                                                         ▒ █
 █ █                                                                                                                █ █
 █ ▒              # Cada pixel do watermark escreve 4 bytes (RGBA)                                                  ▒ █
 █▒▒              # x_pos controla ONDE escrevemos                                                                  ▒▒█
 █ ▒              # pixel values controlam O QUE escrevemos                                                         ▒ █
 █ █                                                                                                                █ █
 █ ▒              watermark_pixel = (R, G, B, A)                                                                    ▒ █
 █▒▒ ¡                               |  |  |  |                                                                     ▒▒█
 █ ▒,█▄                              v  v  v  v                                                                     ▒ █
 █ █▀▀°                            [b0,b1,b2,b3]  # Little-endian                                                   █ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          O filtro que resolvi explorar foi o _composite.c porque depois de tentar                              █ █
 █ ▒          varios, ele foi o mais "simples", que permite mais "controle". e bote aspas.                          ▒ █
 █▒▒          Mas todos tem vulnerabilidades.                                                                       ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          #define ALPHA_COMPOSITE_COLOR_CHANNEL(color1, alpha1, color2, alpha2) \                               █ █
 █ ▒              ( ((1.0 - (alpha1 / MAX_RGB_DOUBLE)) * (double) color1) + \                                       ▒ █
 █▒▒                ((1.0 - (alpha2 / MAX_RGB_DOUBLE)) * (double) color2 * (alpha1 / MAX_RGB_DOUBLE)) )             ▒▒█
 █ ▒          #define ADJUST_COLOR_DOUBLE(c) ((int)((c > MAX_RGB_DOUBLE) ? MAX_RGB : ((c < 0.0) ? 0 :               ▒ █
 █ █          c)))                                                                                                  █ █
 █ ▒                                                                                                                ▒ █
 █▒▒              // _composite.c                                                                                 ¡ ▒▒█
 █ ▒               int width1, width2,                                                                           ▄█,▒ █
 █ █                  height1, height2,                                                                          °▀▀█ █
 █▒▒                  x_pos, y_pos,                                                                                 ▒▒█
 █ ▒                  merge = 1;                                                                                    ▒ █
 █ █              ...                                                                                               █ █
 █ ▒                  int line_offset1 = ((y_pos + y - start_y) * width1 * num_bytes),                              ▒ █
 █▒▒                      line_offset2 = (y * width2 * num_bytes);                                                  ▒▒█
 █ ▒              ...                                                                                               ▒ █
 █ █              if (merge) {                                                                                      █ █
 █ ▒                          delta = (a2 / MAX_RGB_DOUBLE) * (a1 / MAX_RGB_DOUBLE);                                ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                          a = MAX_RGB_DOUBLE * delta;                                                           ▒ █
 █ █                                                                                                                █ █
 █ ▒                          delta = 1.0 - delta;                                                                  ▒ █
 █▒▒ ¡                        delta = (delta <= SMALL_DOUBLE) ? 1.0 : (1.0 / delta);                                ▒▒█
 █ ▒,█▄                                                                                                             ▒ █
 █ █▀▀°                       r = delta * ALPHA_COMPOSITE_COLOR_CHANNEL(r2, a2, r1, a1);                            █ █
 █▒▒                          g = delta * ALPHA_COMPOSITE_COLOR_CHANNEL(g2, a2, g1, a1);                            ▒▒█
 █ ▒                          b = delta * ALPHA_COMPOSITE_COLOR_CHANNEL(b2, a2, b1, a1);                            ▒ █
 █ █                      }                                                                                         █ █
 █ ▒              ...                                                                                               ▒ █
 █▒▒                      r1 = aux1[r_idx];                                                                         ▒▒█
 █ ▒                      g1 = aux1[g_idx];                                                                         ▒ █
 █ █                      b1 = aux1[b_idx];                                                                         █ █
 █ ▒                      a1 = aux1[a_idx];                                                                         ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                      r2 = aux2[r_idx];                                                                         ▒ █
 █ █                      g2 = aux2[g_idx];                                                                         █ █
 █ ▒                      b2 = aux2[b_idx];                                                                         ▒ █
 █▒▒                      a2 = aux2[a_idx];                                                                       ¡ ▒▒█
 █ ▒                                                                                                             ▄█,▒ █
 █ █              ...                                                                                            °▀▀█ █
 █▒▒                      aux1[r_idx] = ADJUST_COLOR_DOUBLE(r);                                                     ▒▒█
 █ ▒                      aux1[g_idx] = ADJUST_COLOR_DOUBLE(g);                                                     ▒ █
 █ █                      aux1[b_idx] = ADJUST_COLOR_DOUBLE(b);                                                     █ █
 █ ▒                      aux1[a_idx] = ADJUST_COLOR_DOUBLE(a);                                                     ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          o aux1 eh onde conseguimos o overflow e apontar para o propio PyBytesObject, o                        █ █
 █ ▒          problema eh o ALPHA_COMPOSITE_COLOR_CHANNEL que mistura as cores das duas                             ▒ █
 █▒▒          imagens de input (o filtro _composite junta duas imagens), entao so conseguimos                       ▒▒█
 █ ▒          total controle do que sera escrito se o valor do alpha (a opacidade)                                  ▒ █
 █ █          for 0xff ou 0x00, porque o pixel vai substituir o anterior ou sera ignorado.                          █ █
 █ ▒                                                                                                                ▒ █
 █▒▒ ¡        Isso tudo pra falar que a gente precisa que o byte[3] que queremos escrever                           ▒▒█
 █ ▒,█▄       TEM que ser 0xff ou 0x00, senao o valor escrito sera diferente. O problema?                           ▒ █
 █ █▀▀°       A gente ta tentando escrever um valor de ponteiro e precisamos que um dos                             █ █
 █▒▒          valores seja 0xff, ~0.8% dos layouts funcionam.                                                       ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █              Restrição do ASLR                                                                                 █ █
 █ ▒                                                                                                                ▒ █
 █▒▒                PyByteArray_Type = 0x00 00 7f XX ?? ?? ?? ??                                                    ▒▒█
 █ ▒                                              ^                                                                 ▒ █
 █ █                                              |                                                                 █ █
 █ ▒                 Escrevemos 4 bytes (RGBA) por vez.                                                             ▒ █
 █▒▒                 O byte que cai na posicao do alpha (byte[3]                                                    ▒▒█
 █ ▒                 do grupo de 4) precisa ser 0x00 ou 0xFF                                                        ▒ █
 █ █                 para o blend nao corromper o valor.                                                            █ █
 █ ▒                                                                                                                ▒ █
 █▒▒              Probabilidade: 2/256 ~ 0.8%                                                                     ¡ ▒▒█
 █ ▒                                                                                                             ▄█,▒ █
 █ █                                                                                                             °▀▀█ █
 █▒▒          Como vamos resolver? Tentando e tentando ate dar certo ( ╥ω╥ ) Umas 300 vezes                         ▒▒█
 █ ▒          deve dar.                                                                                             ▒ █
 █ █                                                                                                                █ █
 █ ▒          5 - Ideias para RCE                                                                                   ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒              // Include/cpython/bytesobject.h                                                                  ▒ █
 █ █              typedef struct {                                                                                  █ █
 █ ▒                  PyObject_VAR_HEAD      // ob_refcnt, ob_type, ob_size                                         ▒ █
 █▒▒                  Py_hash_t ob_shash;    //  hash                                                               ▒▒█
 █ ▒                  char ob_sval[1];       // Buffer                                                              ▒ █
 █ █              } PyBytesObject;                                                                                  █ █
 █ ▒                                                                                                                ▒ █
 █▒▒ ¡            Offset  Campo         Tamanho                                                                     ▒▒█
 █ ▒,█▄           ------  -----         -------                                                                     ▒ █
 █ █▀▀°           +0      ob_refcnt     8 bytes                                                                     █ █
 █▒▒              +8      ob_type       8 bytes  <-- ALVO PRINCIPAL                                                 ▒▒█
 █ ▒              +16     ob_size       8 bytes                                                                     ▒ █
 █ █              +24     ob_shash      8 bytes                                                                     █ █
 █ ▒              +32     ob_sval[0]    ... (buffer de pixels)                                                      ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          Existem algumas maneiras ja documentadas de conseguir RCE nesse contexto de                           █ █
 █ ▒          sobrescrita de um Python Object [5], aqui nao estamos inventando nada, so tentando                    ▒ █
 █▒▒          aplicar a tecnica dado as nossas restricoes. Mas para qualquer tecnica dar                            ▒▒█
 █ ▒          certo precisamos primeiro uma leitura "arbitraria" para descobrir os valores dos                      ▒ █
 █ █          ponteiros. [14] [15]                                                                                  █ █
 █ ▒                                                                                                                ▒ █
 █▒▒          5.1 - Type Confusion: PyBytes -> PyByteArray                                                        ¡ ▒▒█
 █ ▒                                                                                                             ▄█,▒ █
 █ █              PyBytesObject (antes):          PyByteArrayObject (depois):                                    °▀▀█ █
 █▒▒              +--------------------+          +--------------------+                                            ▒▒█
 █ ▒              | ob_refcnt          |          | ob_refcnt          |                                            ▒ █
 █ █              | ob_type -----------+--------->| PyByteArray_Type   |                                            █ █
 █ ▒              | ob_size            |          | ob_size            |                                            ▒ █
 █▒▒              | ob_shash <---------+--------->| ob_alloc           |                                            ▒▒█
 █ ▒              | ob_sval[0...] <----+--------->| ob_bytes (ptr!)    |                                            ▒ █
 █ █              +--------------------+          +--------------------+                                            █ █
 █ ▒                                                      |                                                         ▒ █
 █▒▒                                                      v                                                         ▒▒█
 █ ▒                                                Arbitrary Read!                                                 ▒ █
 █ █                                                                                                                █ █
 █ ▒              PyByteArrayObject tambem tem ob_exports e ob_start                                                ▒ █
 █▒▒ ¡            que nao existem em PyBytesObject. Dependendo da versao do                                         ▒▒█
 █ ▒,█▄           CPython o overlap pode nao ser tao limpo assim. Testado                                           ▒ █
 █ █▀▀°           no CPython 3.11 que o Thumbor usa.                                                                █ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          Uma das ideias eh primeiro apontar o ob_type para uma PyByteArrayObject                               █ █
 █ ▒          porque daria para ler os bytes subsequentes, poderiamos fazer o ob_size ser                           ▒ █
 █▒▒          gigante para tentar ler os ponteiros dos proximos objetos na heap, ou apontar o                       ▒▒█
 █ ▒          ob_bytes para um outro objeto. Infelizmente, depois de horas debuggando, me                           ▒ █
 █ █          parece que pelo tamanho do nosso buffer (4GB) ele esta sendo alocado pelo malloc                      █ █
 █ ▒          e nao pelo pymalloc, que so eh usado se o objeto for menor que 512 bytes [13].                        ▒ █
 █▒▒          Dessa forma o nosso objeto fica numa pagina so pra ele e nao encontrei nenhum                         ▒▒█
 █ ▒          objeto depois que a gente pudesse ler :`(                                                             ▒ █
 █ █                                                                                                                █ █
 █ ▒          6 - Tentando Leak: Sharpen e ByteArray                                                                ▒ █
 █▒▒                                                                                                              ¡ ▒▒█
 █ ▒          O type confusion funciona, mas sem um infoleak nao sabemos os enderecos que                        ▄█,▒ █
 █ █          precisamos escrever. Tentei procurar nos outros filtros se a gente conseguiria                     °▀▀█ █
 █▒▒          pelo menos um infoleak remoto. O wavelet_sharpen pareceu interessante.                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          6.1 - Sharpen Heap Overflow                                                                           █ █
 █ ▒                                                                                                                ▒ █
 █▒▒              // wavelet_sharpen.c - hat_transform()                                                            ▒▒█
 █ ▒              void hat_transform(FLOAT *temp, FLOAT *base, int st, int size, int sc) {                          ▒ █
 █ █                  for (i = 0; i < size; i++) {                                                                  █ █
 █ ▒                      temp[i] = 2*base[st*i] + base[st*(sc-i)] + base[st*(i+sc)];                               ▒ █
 █▒▒                      //                       ^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^                                ▒▒█
 █ ▒                      //                       OOB quando sc > size!                                            ▒ █
 █ █                  }                                                                                             █ █
 █ ▒              }                                                                                                 ▒ █
 █▒▒ ¡                                                                                                              ▒▒█
 █ ▒,█▄                                                                                                             ▒ █
 █ █▀▀°       Com width=3000, height=1:                                                                             █ █
 █▒▒                                                                                                                ▒▒█
 █ ▒              Buffer: 3000 floats (12KB)                                                                        ▒ █
 █ █              Level 4 (sc=16): acessa base[width*31] = base[93000]                                              █ █
 █ ▒                               -> 360KB alem do buffer!                                                         ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          Infelizmente os dados que sao lidos sao tratados como pixels e floats [8], fazendo                    █ █
 █ ▒          a gente perder informacao.                                                                            ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                         Float Interpretation Problem                                                           ▒ █
 █ █                                                                                                                █ █
 █ ▒                Heap contem: 0x7f12 3456 789a bcde (libc pointer)                                               ▒ █
 █▒▒                                                                                                              ¡ ▒▒█
 █ ▒                Interpretado como float (IEEE 754):                                                          ▄█,▒ █
 █ █                bytes [0:4] = 0x789abcde -> float ~ 2.3e+34                                                  °▀▀█ █
 █▒▒                bytes [4:8] = 0x7f123456 -> float ~ 1.9e+38                                                     ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █                Apos 5 niveis wavelet (x0.25^5):                                                                █ █
 █ ▒                -> Valor final ~ 10^30 -> SATURA para pixel 255                                                 ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          Tive ate uma ideia de tentar usar isso como oraculo de ponteiro e diminuir o                          █ █
 █ ▒          brute force necessario, mas nao adiantou pra muita coisa.                                             ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒              Ponteiro na heap produz assinatura caracteristica:                                                ▒ █
 █ █                                                                                                                █ █
 █ ▒                       bytes:  [low] [low] [low] [low] [0x7f] [XX] [00] [00]                                    ▒ █
 █▒▒ ¡                               |     |     |     |      |     |    |    |                                     ▒▒█
 █ ▒,█▄               float        : +-----+-----+-----+      |     |    |    |                                     ▒ █
 █ █▀▀°                              |     +-----+-----+------+     |    |    |                                     █ █
 █▒▒                                 |     |     +-----+------+-----+    |    |                                     ▒▒█
 █ ▒                                 |     |     |     +------+-----+----+    |                                     ▒ █
 █ █                                 |     |     |     |      +-----+----+----+                                     █ █
 █ ▒                                 v     v     v     v      v     v    v    v                                     ▒ █
 █▒▒                  pixels:       [~0]  [~0]  [~0]  [~0]  [255] [255] [~0] [~0]                                   ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █                  Padrao detectavel: [..., 0, 0, 255, 0, 0, ...]                                                █ █
 █ ▒                                                                                                                ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          6.2 - qq a gente faz                                                                                  ▒ █
 █ █                                                                                                                █ █
 █ ▒          Outra ideia que tive foi usar o fato que controlamos o tamanho da imagem para                         ▒ █
 █▒▒          forcar o alocador a alocar outra imagem no mesmo lugar da primeira, ja que elas                     ¡ ▒▒█
 █ ▒          tem o mesmo tamanho, dai daria para:                                                               ▄█,▒ █
 █ █                                                                                                             °▀▀█ █
 █▒▒          1. fazer um type confusion para ByteArray                                                             ▒▒█
 █ ▒          2. apontar para si mesmo para descobrir o ponteiro do nosso propio buffer                             ▒ █
 █ █          3. mandar outra imagem com o fake PyTypeObject dentro dela e fazer overwrite                          █ █
 █ ▒          para o ob_type apontar para dentro do propio buffer, ja que agora sabemos o                           ▒ █
 █▒▒          endereco reutilizado, daria ate pra fazer heap spray com varios fake obj dentro                       ▒▒█
 █ ▒          da imagem.                                                                                            ▒ █
 █ █                                                                                                                █ █
 █ ▒          --[ 7 - A teoria do RCE                                                                               ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          Isso aqui eh praticamente teorico.                                                                    ▒ █
 █ █          O que eu tenho eh cada stage individual funcionando no GDB com                                        █ █
 █ ▒          breakpoints, enderecos hardcoded e muita paciencia. Stage 0 precisa de varios                         ▒ █
 █▒▒ ¡        crashes ate funcionar. E mesmo com acesso local, cada tentativa reinicia o                            ▒▒█
 █ ▒,█▄       processo inteiro.Nao eh um one-shot, eh mais um                                                       ▒ █
 █ █▀▀°       "se todas as estrelas se alinharem coisas interessantes acontecem com a heap do                       █ █
 █▒▒          CPython via HTTP".                                                                                    ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █              +-------------------------------------------------------------+                                   █ █
 █ ▒              |                    EXPLOIT CHAIN                            |                                   ▒ █
 █▒▒              +-------------------------------------------------------------+                                   ▒▒█
 █ ▒              |                                                             |                                   ▒ █
 █ █              |  STAGE 0: Encontrar ASLR Favoravel                          |                                   █ █
 █ ▒              |  +--------------------------------------------------------+ |                                   ▒ █
 █▒▒              |  | Loop:                                                  | |                                   ▒▒█
 █ ▒              |  |   1. Crashar/reiniciar thumbor                         | |                                   ▒ █
 █ █              |  |   2. GDB: checar PyByteArray_Type byte[3]              | |                                   █ █
 █ ▒              |  |   3. Se 0x00 ou 0xFF -> EXPLOITAVEL                    | |                                   ▒ █
 █▒▒              |  |   4. Senao -> restart (sim, de novo)                   | |                                 ¡ ▒▒█
 █ ▒              |  |   5. Salvar enderecos: PyByteArray_Type, system()      | |                                ▄█,▒ █
 █ █              |  | Media: ~128 tentativas (na pratica fiz ~200)           | |                                °▀▀█ █
 █▒▒              |  |                                                        | |                                   ▒▒█
 █ ▒              |  +--------------------------------------------------------+ |                                   ▒ █
 █ █              |                           |                                 |                                   █ █
 █ ▒              |                           v                                 |                                   ▒ █
 █▒▒              |  STAGE 1: Type Confusion                                    |                                   ▒▒█
 █ ▒              |  +--------------------------------------------------------+ |                                   ▒ █
 █ █              |  | HTTP Request:                                          | |                                   █ █
 █ ▒              |  | /65500x16394/filters:watermark(wm.png,318,16393,0)/    | |                                   ▒ █
 █▒▒              |  |                                                        | |                                   ▒▒█
 █ ▒              |  | Integer overflow em _composite.c:                      | |                                   ▒ █
 █ █              |  |   16393 * 65500 * 4 = 4294966000                       | |                                   █ █
 █ ▒              |  |   (int32) -> -1296                                     | |                                   ▒ █
 █▒▒ ¡            |  |                                                        | |                                   ▒▒█
 █ ▒,█▄           |  | Watermark pixels sobrescrevem o header:                | |                                   ▒ █
 █ █▀▀°           |  |   x=318 -> offset -24 (ob_type)                        | |                                   █ █
 █▒▒              |  |   Pixel RGBA = bytes low de PyByteArray_Type           | |                                   ▒▒█
 █ ▒              |  |   byte[3] precisa ser 0x00 ou 0xFF (filtrado no S0)    | |                                   ▒ █
 █ █              |  |                                                        | |                                   █ █
 █ ▒              |  | Resultado: ob_type agora aponta para PyByteArray_Type  | |                                   ▒ █
 █▒▒              |  | O objeto PyBytes agora eh um PyByteArray               | |                                   ▒▒█
 █ ▒              |  +--------------------------------------------------------+ |                                   ▒ █
 █ █              |                           |                                 |                                   █ █
 █ ▒              |                           v                                 |                                   ▒ █
 █▒▒              |  STAGE 2: Self-leak do endereco do buffer                   |                                   ▒▒█
 █ ▒              |  +--------------------------------------------------------+ |                                   ▒ █
 █ █              |  | Apos type confusion, o campo ob_sval (offset +32) eh   | |                                   █ █
 █ ▒              |  | reinterpretado como ob_bytes (ponteiro para dados).    | |                                   ▒ █
 █▒▒              |  |                                                        | |                                 ¡ ▒▒█
 █ ▒              |  | Se ob_bytes aponta para o proprio buffer, lendo o      | |                                ▄█,▒ █
 █ █              |  | "bytearray" de volta via resposta HTTP podemos         | |                                °▀▀█ █
 █▒▒              |  | reconstruir o endereco do nosso buffer na heap.        | |                                   ▒▒█
 █ ▒              |  |                                                        | |                                   ▒ █
 █ █              |  | PROBLEMA: na pratica eu nao consegui confirmar que o   | |                                   █ █
 █ ▒              |  | Thumbor retorna os bytes crus do bytearray "confused"  | |                                   ▒ █
 █▒▒              |  | na resposta HTTP. O PIL pode rejeitar/corromper os     | |                                   ▒▒█
 █ ▒              |  | dados antes.                                           | |                                   ▒ █
 █ █              |  +--------------------------------------------------------+ |                                   █ █
 █ ▒              |                           |                                 |                                   ▒ █
 █▒▒              |                           v                                 |                                   ▒▒█
 █ ▒              |  STAGE 3: Fake PyTypeObject                                 |                                   ▒ █
 █ █              |  +--------------------------------------------------------+ |                                   █ █
 █ ▒              |  | Nova imagem com MESMO tamanho -> alocador reutiliza    | |                                   ▒ █
 █▒▒ ¡            |  | o mesmo endereco (agora conhecido do Stage 2).         | |                                   ▒▒█
 █ ▒,█▄           |  |                                                        | |                                   ▒ █
 █ █▀▀°           |  | Pixels da imagem contem:                               | |                                   █ █
 █▒▒              |  |   1. Fake PyTypeObject no offset conhecido:            | |                                   ▒▒█
 █ ▒              |  |      - tp_dealloc (+48) = endereco de system() [15]    | |                                   ▒ █
 █ █              |  |      - restante dos slots = NULL                       | |                                   █ █
 █ ▒              |  |   2. Comando shell nos primeiros bytes do objeto:      | |                                   ▒ █
 █▒▒              |  |      ob_refcnt = "touch /t" (8 bytes)(PIL bypass?)     | |                                   ▒▒█
 █ ▒              |  |      ob_type   = ponteiro para fake type no buffer     | |                                   ▒ █
 █ █              |  |                                                        | |                                   █ █
 █ ▒              |  | Alternativas para o slot de execucao:                  | |                                   ▒ █
 █▒▒              |  |   tp_dealloc (+48): trigger = refcnt chega a zero      | |                                   ▒▒█
 █ ▒              |  |     - risco: deallocs de outros objs podem crashar     | |                                   ▒ █
 █ █              |  |   tp_repr    (+88): trigger = repr(obj) ou print(obj)  | |                                   █ █
 █ ▒              |  |   tp_getattro(+144): trigger = obj.qualquer_attr [14]  | |                                   ▒ █
 █▒▒              |  |     - mais confiavel, nao depende de refcnt            | |                                 ¡ ▒▒█
 █ ▒              |  |     - RDI = obj_ptr, entao obj comeca com "/bin/sh\0"  | |                                ▄█,▒ █
 █ █              |  +--------------------------------------------------------+ |                                °▀▀█ █
 █▒▒              |                           |                                 |                                   ▒▒█
 █ ▒              |                           v                                 |                                   ▒ █
 █ █              |  STAGE 4: Trigger                                           |                                   █ █
 █ ▒              |  +--------------------------------------------------------+ |                                   ▒ █
 █▒▒              |  | O integer overflow novamente sobrescreve ob_type para  | |                                   ▒▒█
 █ ▒              |  | apontar para o fake PyTypeObject dentro do buffer.     | |                                   ▒ █
 █ █              |  |                                                        | |                                   █ █
 █ ▒              |  | Quando o Thumbor termina de processar a imagem:        | |                                   ▒ █
 █▒▒              |  |   - tp_dealloc: chamado ao liberar o objeto            | |                                   ▒▒█
 █ ▒              |  |     system("touch /t...") (comando nos bytes iniciais) | |                                   ▒ █
 █ █              |  |   - tp_getattro: chamado se qualquer atributo for      | |                                   █ █
 █ ▒              |  |     acessado antes da liberacao                        | |                                   ▒ █
 █▒▒ ¡            |  |     system("/bin/sh\0...") (ob_refcnt = comando)       | |                                   ▒▒█
 █ ▒,█▄           |  +--------------------------------------------------------+ |                                   ▒ █
 █ █▀▀°           |                           |                                 |                                   █ █
 █▒▒              |                           v                                 |                                   ▒▒█
 █ ▒              |                     +---------+                             |                                   ▒ █
 █ █              |                     |   RCE   |                             |                                   █ █
 █ ▒              |                     +---------+                             |                                   ▒ █
 █▒▒              +-------------------------------------------------------------+                                   ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █                                                                                                                █ █
 █ ▒          8 - Conclusao                                                                                         ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒          Sem PoC funcional pro 0day (nao resolvi o infoleak kkkkk).                                            ▒ █
 █ █          Daria um chall legal de CTF.                                                                          █ █
 █ ▒                                                                                                                ▒ █
 █▒▒          9 - Referencias                                                                                     ¡ ▒▒█
 █ ▒                                                                                                             ▄█,▒ █
 █ █              [1] HMAC - Keyed-Hashing for Message Authentication                                            °▀▀█ █
 █▒▒                  https://pt.wikipedia.org/wiki/HMAC                                                            ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █              [2] Leurent, G., & Peyrin, T. (2020). SHA-1 is a Shambles.                                        █ █
 █ ▒                  USENIX Security 2020.                                                                         ▒ █
 █▒▒                  https://sha-mbles.github.io/                                                                  ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █              [3] Stevens, M., et al. (2017). The first collision for full SHA-1.                               █ █
 █ ▒                  https://shattered.io/                                                                         ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒              [4] CPython Source Code - bytesobject.c                                                           ▒ █
 █ █                  https://github.com/python/cpython/blob/main/Objects/bytesobject.c                             █ █
 █ ▒                                                                                                                ▒ █
 █▒▒ ¡            [5] CPython Internals Book                                                                        ▒▒█
 █ ▒,█▄               https://realpython.com/cpython-source-code-guide/                                             ▒ █
 █ █▀▀°                                                                                                             █ █
 █▒▒              [6] Phrack - "Once upon a free()"                                                                 ▒▒█
 █ ▒                  http://phrack.org/issues/57/9.html                                                            ▒ █
 █ █                                                                                                                █ █
 █ ▒              [7] glibc malloc internals                                                                        ▒ █
 █▒▒                  https://sourceware.org/glibc/wiki/MallocInternals                                             ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █              [8] IEEE 754 Floating Point                                                                       █ █
 █ ▒                  https://en.wikipedia.org/wiki/IEEE_754                                                        ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒              [9] CWE-190: Integer Overflow                                                                     ▒ █
 █ █                  https://cwe.mitre.org/data/definitions/190.html                                               █ █
 █ ▒                                                                                                                ▒ █
 █▒▒              [10] Thumbor Documentation                                                                      ¡ ▒▒█
 █ ▒                   https://thumbor.readthedocs.io/                                                           ▄█,▒ █
 █ █                                                                                                             °▀▀█ █
 █▒▒              [11] Thumbor Source Code                                                                          ▒▒█
 █ ▒                   https://github.com/thumbor/thumbor                                                           ▒ █
 █ █                                                                                                                █ █
 █ ▒              [12] Pillow (Python Imaging Library Fork)                                                         ▒ █
 █▒▒                   https://github.com/python-pillow/Pillow                                                      ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █              [13] The pymalloc allocator - CPython docs                                                        █ █
 █ ▒                   https://docs.python.org/3/c-api/memory.html#the-pymalloc-allocator                           ▒ █
 █▒▒                                                                                                                ▒▒█
 █ ▒              [14] kn32 - Exploiting a Use-After-Free for code execution in                                     ▒ █
 █ █                   every version of Python 3                                                                    █ █
 █ ▒                   https://pwn.win/2022/05/11/python-buffered-reader.html                                       ▒ █
 █▒▒ ¡                                                                                                              ▒▒█
 █ ▒,█▄           [15] bebop404 - CPython UAF: Exploit Chain Dev from UAF Trigger to RCE                            ▒ █
 █ █▀▀°                https://www.bebop404.com/blog/cpython-uaf-exploit-chain                                      █ █
 █▒▒                                                                                                                ▒▒█
 █ ▒                                                                                                                ▒ █
 █ █          |=[ EOF ]=---------------------------------------------------------------=|                           █ █
 █ ▒                                                                                                                ▒ █
 █▒▒                                                                                                                ▒▒█
 █▒▒▒▒▒ ░░                                                                                                    ▒▒▒▒▒▒▒▒█
 █ ▒                                                                                                                ▓▄█
 █ █                                    T R A M O I A   ·   Z I N E   ·  2 0 2 6                                 gld ██
  ▀▄▄▄▄                                                                                                          ▒▒▄▄▀