---
# System prepended metadata

title: CAOV record

---

# CAOV record
pwnable.tw 中的 CAOV 一題，因為有附 cpp 檔案，所以我想說會不會比較好做一點...事實證明我錯了
## 時間:
2019/07/28

## 分析
程式不大，總的來說他實做了一個 Date class ，先不管 method 只看 member:
```cpp=
class Data
{
    private:
        char *key; // store string
        long value; // store value
        long change_count; // store edit count
        int year; // store time
        int month;
        int day;
        int hour;
        int min;
        int sec;
        void init_time()
        {
            year  = 0;
            month = 0;
            day   = 0;
            hour  = 0;
            min   = 0;
            sec   = 0;
        }
};
```

光是 constructor 就有三個:
```cpp
// 1
Data():key(NULL) , value(0), change_count(0){ init_time(); }
// 2
Data(string k, int v)
        {
            key = new char[k.length() + 1];
            strcpy(key, k.c_str());
            value = v;
            change_count = 0;
            update_time();
        }
// 3
Data(const Data &obj)
        {
            key = new char[strlen(obj.key)+1];
            strcpy(key, obj.key);
            value = obj.value;
            change_count = obj.change_count;
            year  = obj.year;
            month = obj.month;
            day   = obj.day;
            hour  = obj.hour;
            min   = obj.min;
            sec   = obj.sec;
        }
```
另外還有一個 operator overloading
```cpp
Data operator=(const Data &rhs)
        {
            key = new char[strlen(rhs.key)+1];
            strcpy(key, rhs.key);
            value = rhs.value;
            change_count = rhs.change_count;
            year  = rhs.year;
            month = rhs.month;
            day   = rhs.day;
            hour  = rhs.hour;
            min   = rhs.min;
            sec   = rhs.sec;
        }
```
跟修改比較有關系的是:
```cpp
        void edit_data()
        {
            if(change_count == 10)
            {
                cout << "You can only edit your data 10 times at most." << endl;
                cout << "Bye ._.\\~/" << endl;
                exit(0);
            }
            int old_len = strlen(key);
            unsigned int new_len = 0;
            cout << "New key length: ";
            cin  >> new_len;
            getchar();
            if(new_len == 0 || new_len > 1000)
            {
                cout << "Invalid key length" << endl;
                return;
            }
            if (new_len > old_len) key = new char[new_len+1];
            set_data(new_len);
            change_count += 1;
        }   
```
上面是修改 class 用的，外面還有個 wrapper 之類的東西:
```cpp
void edit()
{
    Data old;
    old = *D;
    D->edit_data();
    cout << "\nYour data info before editing:" << endl;
    old.info();
    cout << "\nYour data info after editing:" << endl;
    D->info();
}
```
destructor
```cpp
        ~Data()
        {
            delete[] key;
            key = nullptr;
            value = 0;
            change_count = 0;
            init_time();
        }
```
說不大，但全部貼上來還是滿大的，我只截錄幾個我覺得可疑的

全域變數:
```cpp
Data *D;
char name[160];
```
讀 name 的部分特別實作一個 function:
```cpp
void set_name()
{
    char tmp[160]={};
    char c;
    cout << "Enter your name: ";
    int cnt = 0;
    while(1)
    {
        int len = read(0, &c, 1);
        if(len != 1)
        {
            cout << "Read error" << endl;
            exit(-1);
        }
        tmp[cnt++] = c;
        if(c == '\n' || cnt == 150)
        {
            tmp[cnt-1] = '\0';
            break;            
        }
    }
    memcpy(name, tmp, cnt);
}
```

## 漏洞:
因為太廢 source review 看不出來，我直接用 fuzz 找到在 edit 的 operator overloading 會把 stack 上的殘留值當作 object ，然後根據 destructor:
```cpp=
        ~Data()
        {
            delete[] key;
            key = nullptr;
            value = 0;
            change_count = 0;
            init_time();
        }
```
line 3 會把 stack 存的殘留當作 key free 掉，這邊有個任意地址 free 漏洞，因為前一個用到這空間的是 set_name 中的 tmp ，所以地址可控

根據任意地址 free 只想到 house of spirit ，但需要知道要 free 掉空間地址，好像只有 global 符合

## 進度
目前成功把 global 空間當作 heap 送進 free list ，但下一步怎麼利用還沒想到...

### 筆記
1. 目前能做到任意 free ，要想如何切換到任意讀取寫入漏洞

2. 能 allocate 的好像只有 key 這個陣列，而只有在 constructor 和 operator overloading 的部分才有 allocate 和 free 

**假設**: 如果可以藉由 double free 改 fastbin 讓 chunk overlap 來洩漏 libc 呢？

### 結果
1. 首先先偽造一個 0x70 大小的 chunk 在 name 上，然後透過任意 free 漏洞將其放入 fastbin

2. edit 前面的 set_name 可以修改已經在 fastbin 中的 fake chunk 的 fd ，將其改到同樣位於 global 中的 stdout/stdin (選一個)，因為大小為 0x7f ，故 size 符合
3. 再 edit 以 key 的身分將其領出，並覆蓋掉後面的 class obj pointer 為 name array 中的某地址 A ，此地址 A 為 fake obj ptr
4. 再 edit 將 A 指向的地址( key array )改成 got 地址
5. show() 洩漏出 glibc address
6. 再 edit 修改 name 時改掉被 A 指向的地址的地址為 _IO_2_1_stderr_ 結構的 fp ，改為 "sh"
7. edit 修改 name 時改掉被 A 指向的地址的地址為 write_base 為 0 (要 bypass 撿查，觸發 _IO_fflush)
8. 操作基本同上，但這次改成 write_ptr 為 1 ，一樣要 bypass check
9. 操作基本同上，但這次改成 vtable ，將其改為 name array 某地址
10. edit 的時候對剛剛 vtable 的地址 + IO_overflow 偏移改為 system 
11. 之後會有 double free 觸發 overflow() 
12. get shell

## 困難
做到任意地址寫入後，我想過修改 free_hook : 將 free_hook 前面的空間當 chunk 放入 fastbin 中再領出使用 => 結果 free_hook 前面空間的值會改變 = =

全部弄好後我想說本地實驗一下，結果在 time() 的時候就掛掉了，就直接 remote 過去


