輸入驗證和表示法問題是由中繼字元、替代編碼和數值表示法引起的。信任輸入會導致安全問題。問題包括:「Buffer Overflows」、「Cross-Site Scripting」攻擊、「SQL Injection」及其他許多問題。
Value Stack
內容中評估 EL 運算式。允許根據 Value Stack
來評估未驗證的運算式可能會為攻擊者提供修改系統變數或執行任意程式碼的存取權。
OgnlContext ctx = new OgnlContext();
String expression = request.getParameter("input");
Object expr = Ognl.parseExpression(expression);
Object value = Ognl.getValue(expr, ctx, root);
System.out.println("Value: " + value);
(#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc.exe"))
%{expr}
)。攻擊者在控制第一次評估的結果後,即有可能控制要在第二次 OGNL 評估中評估的運算式,進而插入任意 OGNL 運算式。redirectAction
結果會用於評估其參數兩次。在此案例中,攻擊者可透過提供 redirect
要求參數來控制 actionName
參數中強制 OGNL 運算式的結果。
...
<action name="index" class="com.acme.MyAction">
<result type="redirectAction">
<param name="actionName">${#parameters['redirect']}</param>
<param name="namespace">/foo</param>
</result>
</action>
...
%{#parameters['redirect']}
運算式,該運算式會被當作 OGNL 運算式來進行評估,讓攻擊者能夠評估任意 OGNL 運算式。execute()
之外的方法。!
(驚嘆號) 字元或 method:
字首可用於動作 URL 中,在啟用「動態方法叫用」時叫用動作中的任何公用方法。在 Struts 2 版本 2.3.20
中,叫用先前基於反映之替代方法的機制,已改為使用 OGNL 來代替,這讓攻擊者得以提供惡意 OGNL 運算式來取代替代的方法名稱。debug
要求參數來觸發:console
將會快顯 OGNL 評估主控台,以允許開發人員在伺服器上評估任何任意 OGNL 運算式。command
將允許開發人員使用要求參數 expression
提交任意 OGNL 運算式以進行評估。xml
將參數、上下文、階段作業和值堆疊傾印為 XML 文件。browser
將參數、上下文、階段作業和值堆疊傾印為可瀏覽的 HTML 文件。dest
要求參數解析的 URL。
...
DATA: str_dest TYPE c.
str_dest = request->get_form_field( 'dest' ).
response->redirect( str_dest ).
...
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。dest
要求參數所讀取的 URL。
...
var params:Object = LoaderInfo(this.root.loaderInfo).parameters;
var strDest:String = String(params["dest"]);
host.updateLocation(strDest);
...
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。PageReference
物件,其包含來自 dest
要求參數的 URL。
public PageReference pageAction() {
...
PageReference ref = ApexPages.currentPage();
Map<String,String> params = ref.getParameters();
return new PageReference(params.get('dest'));
}
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。dest
要求參數所解析的 URL。
String redirect = Request["dest"];
Response.Redirect(redirect);
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。dest
要求參數剖析的 URL。
...
final server = await HttpServer.bind(host, port);
await for (HttpRequest request in server) {
final response = request.response;
final headers = request.headers;
final strDest = headers.value('strDest');
response.headers.contentType = ContentType.text;
response.redirect(Uri.parse(strDest!));
await response.close();
}
...
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。dest
要求參數剖析的 URL。
...
strDest := r.Form.Get("dest")
http.Redirect(w, r, strDest, http.StatusSeeOther)
...
Example 1
中的程式碼會將瀏覽器重新導向至 "http://www.wilyhacker.com"。dest
要求參數所解析的 URL。
<end-state id="redirectView" view="externalRedirect:#{requestParameters.dest}" />
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。dest
要求參數所讀取的 URL。
...
strDest = form.dest.value;
window.open(strDest,"myresults");
...
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。dest
要求參數所解析的 URL。
<%
...
$strDest = $_GET["dest"];
header("Location: " . $strDest);
...
%>
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。dest
要求參數所解析的 URL。
...
-- Assume QUERY_STRING looks like dest=http://www.wilyhacker.com
dest := SUBSTR(OWA_UTIL.get_cgi_env('QUERY_STRING'), 6);
OWA_UTIL.redirect_url('dest');
...
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。dest
要求參數所解析的 URL。
...
strDest = request.field("dest")
redirect(strDest)
...
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。dest
要求參數所解析的 URL:
...
str_dest = req.params['dest']
...
res = Rack::Response.new
...
res.redirect("http://#{dest}")
...
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。dest
要求參數解析的 URL:
def myAction = Action { implicit request =>
...
request.getQueryString("dest") match {
case Some(location) => Redirect(location)
case None => Ok("No url found!")
}
...
}
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。requestToLoad
設定為指向原始 URL 的「dest」參數 (如果存在的話) 以及指向使用 http://
架構的原始 URL,最後在 WKWebView 內載入此要求:
...
let requestToLoad : String
...
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
...
if let urlComponents = NSURLComponents(URL: url, resolvingAgainstBaseURL: false) {
if let queryItems = urlComponents.queryItems as? [NSURLQueryItem]{
for queryItem in queryItems {
if queryItem.name == "dest" {
if let value = queryItem.value {
request = NSURLRequest(URL:NSURL(string:value))
requestToLoad = request
break
}
}
}
}
if requestToLoad == nil {
urlComponents.scheme = "http"
requestToLoad = NSURLRequest(URL:urlComponents.URL)
}
}
...
}
...
...
let webView : WKWebView
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
webView.loadRequest(appDelegate.requestToLoad)
...
Example 1
中的程式碼將會嘗試在 WKWebView 中要求並載入「http://www.wilyhacker.com」。dest
要求參數所解析的 URL。
...
strDest = Request.Form('dest')
HyperLink.NavigateTo strDest
...
Example 1
中的程式碼會將瀏覽器重新導向至「http://www.wilyhacker.com」。strncpy()
的範圍函數,使用不正確時也會引起弱點。大多數 Buffer overflow 弱點的根本原因,都是緩衝區的處理,加上對資料的大小或組成假設錯誤。memcpy()
呼叫會從 cArray
的分配邊界之外讀取記憶體,其包含了 char
類型的 MAX
元素,而 iArray
則包含了 int
類型的 MAX
元素。範例 2: 以下的簡短程式,在固定的位元長度內,呼叫
void MemFuncs() {
char array1[MAX];
int array2[MAX];
memcpy(array2, array1, sizeof(array2));
}
memchr()
,使用不被信賴的指令行引數做為搜尋緩衝區,進行分析。
int main(int argc, char** argv) {
char* ret = memchr(argv[0], 'x', MAX_PATH);
printf("%s\n", ret);
}
argv[0]
資料,列印 argv[0]
的子字串。但是,(固定) 長度的位元可能會比配置給 argv[0]
的資料長,執行搜尋時,可能會超出配置給 argv[0]
的資料範圍。在 argv[0]
找不到 x
時,正是如上所述的情況。strncpy()
的範圍函數,使用不正確時也會引起弱點。大多數 Buffer overflow 弱點的根本原因,都是緩衝區的處理,加上對資料的大小或組成假設錯誤。char
的五個元素陣列,而最後一個參照則造成了 off-by-one 錯誤。
char Read() {
char buf[5];
return 0
+ buf[0]
+ buf[1]
+ buf[2]
+ buf[3]
+ buf[4]
+ buf[5];
}
strncpy()
的範圍函數,使用不正確時也會引起弱點。大多數 Buffer overflow 弱點的根本原因,都是緩衝區的處理,加上對資料的大小或組成假設錯誤。getInputLength()
讀取的不信賴值要小於目的緩衝區 output
的大小,來嘗試避免邊界外讀取 Buffer Oerflow 的發生。但由於 len
和 MAX
的比較為有符號比較法,所以若 len
為負數值,當其轉換為 memcpy()
的無符號引數時,就會變成非常大的正數值。
void TypeConvert() {
char input[MAX];
char output[MAX];
fillBuffer(input);
int len = getInputLength();
if (len <= MAX) {
memcpy(output, input, len);
}
...
}
...
*Get the report that is to be deleted
r_name = request->get_form_field( 'report_name' ).
CONCATENATE `C:\\users\\reports\\` r_name INTO dsn.
DELETE DATASET dsn.
...
..\\..\\usr\\sap\\DVEBMGS00\\exe\\disp+work.exe
」的檔案名稱,應用程式將刪除關鍵檔案並立即使 SAP 系統當機。
...
PARAMETERS: p_date TYPE string.
*Get the invoice file for the date provided
CALL FUNCTION 'FILE_GET_NAME'
EXPORTING
logical_filename = 'INVOICE'
parameter_1 = p_date
IMPORTING
file_name = v_file
EXCEPTIONS
file_not_found = 1
OTHERS = 2.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
OPEN DATASET v_file FOR INPUT IN TEXT MODE.
DO.
READ DATASET v_file INTO v_record.
IF SY-SUBRC NE 0.
EXIT.
ELSE.
WRITE: / v_record.
ENDIF.
ENDDO.
...
..\\..\\usr\\sap\\sys\\profile\\default.pfl
」的字串而非有效日期,應用程式將顯示所有預設的 SAP 應用程式伺服器設定檔參數設定 - 可能會導致更精確的攻擊。../../tomcat/conf/server.xml
」的可能性,這會導致應用程式刪除本身其中一個組態設定檔案。範例 2:以下程式碼使用來自配置檔案的輸入,來決定要開啟哪個檔案並寫入「除錯」主控台或記錄檔。如果程式需要適當權限才能執行,且惡意使用者可以變更配置檔案,則他們可以使用程式來讀取系統中結尾副檔名為
var params:Object = LoaderInfo(this.root.loaderInfo).parameters;
var rName:String = String(params["reportName"]);
var rFile:File = new File("/usr/local/apfr/reports/" + rName);
...
rFile.deleteFile();
.txt
的任意檔案。
var fs:FileStream = new FileStream();
fs.open(new File(String(configStream.readObject())+".txt"), FileMode.READ);
fs.readBytes(arr);
trace(arr);
public class MyController {
...
public PageRerference loadRes() {
PageReference ref = ApexPages.currentPage();
Map<String,String> params = ref.getParameters();
if (params.containsKey('resName')) {
if (params.containsKey('resPath')) {
return PageReference.forResource(params.get('resName'), params.get('resPath'));
}
}
return null;
}
}
..\\..\\Windows\\System32\\krnl386.exe
」的可能性,這會導致應用程式刪除重要的 Windows 系統檔案。範例 2:以下程式碼使用來自配置檔案的輸入,來決定要開啟哪個檔案並返回給使用者。如果程式需要適當權限才能執行,且惡意使用者可以變更配置檔案,則他們可以使用程式來讀取系統中結尾副檔名為「.txt」的任意檔案。
String rName = Request.Item("reportName");
...
File.delete("C:\\users\\reports\\" + rName);
sr = new StreamReader(resmngr.GetString("sub")+".txt");
while ((line = sr.ReadLine()) != null) {
Console.WriteLine(line);
}
../../apache/conf/httpd.conf
」的可能性,這會導致應用程式刪除特定組態設定檔案。範例 2:以下程式碼使用來自指令行的輸入,來決定要開啟哪個檔案並回傳給使用者。如果程式需要適當權限才能執行,且惡意使用者能夠建立檔案的軟連結,他們就可以使用程式來讀取系統中任何檔案的開始部分。
char* rName = getenv("reportName");
...
unlink(rName);
ifstream ifs(argv[0]);
string s;
ifs >> s;
cout << s;
...
EXEC CICS
WEB READ
FORMFIELD(FILE)
VALUE(FILENAME)
...
END-EXEC.
EXEC CICS
READ
FILE(FILENAME)
INTO(RECORD)
RIDFLD(ACCTNO)
UPDATE
...
END-EXEC.
...
..\\..\\Windows\\System32\\krnl386.exe
」的可能性,這會導致應用程式刪除重要的 Windows 系統檔案。
<cffile action = "delete"
file = "C:\\users\\reports\\#Form.reportName#">
final server = await HttpServer.bind('localhost', 18081);
server.listen((request) async {
final headers = request.headers;
final path = headers.value('path');
File(path!).delete();
}
Example 1
中,未先驗證 headers.value('path')
即對檔案執行刪除功能。../../tomcat/conf/server.xml
」的可能性,這會導致應用程式刪除本身其中一個組態設定檔案。範例 2:以下程式碼使用來自組態設定檔案的輸入,來決定開啟哪個檔案並返回給使用者。如果程式以足夠的權限執行,而且惡意使用者可以變更組態設定檔案,則他們可以使用程式讀取系統上以副檔名
rName := "/usr/local/apfr/reports/" + req.FormValue("fName")
rFile, err := os.OpenFile(rName, os.O_RDWR|os.O_CREATE, 0755)
defer os.Remove(rName);
defer rFile.Close()
...
.txt
結尾的任何檔案。
...
config := ReadConfigFile()
filename := config.fName + ".txt";
data, err := ioutil.ReadFile(filename)
...
fmt.Println(string(data))
../../tomcat/conf/server.xml
」的可能性,這會導致應用程式刪除本身其中一個組態設定檔案。範例 2:以下程式碼使用來自配置檔案的輸入,來決定要開啟哪個檔案並返回給使用者。如果程式需要適當權限才能執行,且惡意使用者可以變更配置檔案,則他們可以使用程式來讀取系統中結尾副檔名為
String rName = request.getParameter("reportName");
File rFile = new File("/usr/local/apfr/reports/" + rName);
...
rFile.delete();
.txt
的任意檔案。
fis = new FileInputStream(cfg.getProperty("sub")+".txt");
amt = fis.read(arr);
out.println(arr);
Example 1
以適用於 Android 平台。
...
String rName = this.getIntent().getExtras().getString("reportName");
File rFile = getBaseContext().getFileStreamPath(rName);
...
rFile.delete();
...
../../tomcat/conf/server.xml
」的可能性,這會導致應用程式刪除本身其中一個組態設定檔案。範例 2:以下程式碼使用來自本機儲存的輸入,來決定開啟哪個檔案並返回給使用者。如果惡意使用者能夠變更本機儲存的內容,便可以使用程式來讀取系統上副檔名為
...
var reportNameParam = "reportName=";
var reportIndex = document.indexOf(reportNameParam);
if (reportIndex < 0) return;
var rName = document.URL.substring(reportIndex+reportNameParam.length);
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
fs.root.getFile('/usr/local/apfr/reports/' + rName, {create: false}, function(fileEntry) {
fileEntry.remove(function() {
console.log('File removed.');
}, errorHandler);
}, errorHandler);
}, errorHandler);
.txt
的任何檔案。
...
var filename = localStorage.sub + '.txt';
function oninit(fs) {
fs.root.getFile(filename, {}, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
var txtArea = document.createElement('textarea');
txtArea.value = this.result;
document.body.appendChild(txtArea);
};
reader.readAsText(file);
}, errorHandler);
}, errorHandler);
}
window.requestFileSystem(window.TEMPORARY, 1024*1024, oninit, errorHandler);
...
../../tomcat/conf/server.xml
」的可能性,這會導致應用程式刪除本身其中一個組態設定檔案。範例 2:以下程式碼使用來自組態設定檔案的輸入,來決定開啟哪個檔案並返回給使用者。如果程式以足夠的權限執行,而且惡意使用者可以變更組態設定檔案,則他們可以使用程式讀取系統上以副檔名
val rName: String = request.getParameter("reportName")
val rFile = File("/usr/local/apfr/reports/$rName")
...
rFile.delete()
.txt
結尾的任何檔案。
fis = FileInputStream(cfg.getProperty("sub").toString() + ".txt")
amt = fis.read(arr)
out.println(arr)
Example 1
以適用於 Android 平台。
...
val rName: String = getIntent().getExtras().getString("reportName")
val rFile: File = getBaseContext().getFileStreamPath(rName)
...
rFile.delete()
...
- (NSData*) testFileManager {
NSString *rootfolder = @"/Documents/";
NSString *filePath = [rootfolder stringByAppendingString:[fileName text]];
NSFileManager *fm = [NSFileManager defaultManager];
return [fm contentsAtPath:filePath];
}
../../tomcat/conf/server.xml
」的可能性,這會導致應用程式刪除本身其中一個組態設定檔案。範例 2:以下程式碼使用來自配置檔案的輸入,來決定要開啟哪個檔案並返回給使用者。如果程式需要適當權限才能執行,且惡意使用者可以變更配置檔案,則他們可以使用程式來讀取系統中結尾副檔名為
$rName = $_GET['reportName'];
$rFile = fopen("/usr/local/apfr/reports/" . rName,"a+");
...
unlink($rFile);
.txt
的任意檔案。
...
$filename = $CONFIG_TXT['sub'] . ".txt";
$handle = fopen($filename,"r");
$amt = fread($handle, filesize($filename));
echo $amt;
...
../../tomcat/conf/server.xml
」的可能性,這會導致應用程式刪除本身其中一個組態設定檔案。範例 2:以下程式碼使用來自配置檔案的輸入,來決定要開啟哪個檔案並返回給使用者。如果程式需要適當權限才能執行,且惡意使用者可以變更配置檔案,則他們可以使用程式來讀取系統中結尾副檔名為
rName = req.field('reportName')
rFile = os.open("/usr/local/apfr/reports/" + rName)
...
os.unlink(rFile);
.txt
的任意檔案。
...
filename = CONFIG_TXT['sub'] + ".txt";
handle = os.open(filename)
print handle
...
../../tomcat/conf/server.xml
」的可能性,這會導致應用程式刪除本身其中一個組態設定檔案。範例 2:以下程式碼使用來自配置檔案的輸入,來決定要開啟哪個檔案並返回給使用者。如果程式需要適當權限才能執行,且惡意使用者可以變更配置檔案,則他們可以使用程式來讀取系統中結尾副檔名為
rName = req['reportName']
File.delete("/usr/local/apfr/reports/#{rName}")
.txt
的任意檔案。
...
fis = File.new("#{cfg.getProperty("sub")}.txt")
amt = fis.read
puts amt
../../tomcat/conf/server.xml
」的可能性,這會導致應用程式刪除本身其中一個組態設定檔案。範例 2:以下程式碼使用來自配置檔案的輸入,來決定要開啟哪個檔案並返回給使用者。如果程式需要適當權限才能執行,且惡意使用者可以變更配置檔案,則他們可以使用程式來讀取系統中結尾副檔名為
def readFile(reportName: String) = Action { request =>
val rFile = new File("/usr/local/apfr/reports/" + reportName)
...
rFile.delete()
}
.txt
的任意檔案。
val fis = new FileInputStream(cfg.getProperty("sub")+".txt")
val amt = fis.read(arr)
out.println(arr)
func testFileManager() -> NSData {
let filePath : String = "/Documents/\(fileName.text)"
let fm : NSFileManager = NSFileManager.defaultManager()
return fm.contentsAtPath(filePath)
}
..\conf\server.xml
」的可能性,這會導致應用程式刪除本身其中一個組態設定檔案。範例 2:以下程式碼使用來自配置檔案的輸入,來決定要開啟哪個檔案並返回給使用者。如果程式需要適當權限才能執行,且惡意使用者可以變更配置檔案,則他們可以使用程式來讀取系統中結尾副檔名為
Dim rName As String
Dim fso As New FileSystemObject
Dim rFile as File
Set rName = Request.Form("reportName")
Set rFile = fso.GetFile("C:\reports\" & rName)
...
fso.DeleteFile("C:\reports\" & rName)
...
.txt
的任意檔案。
Dim fileName As String
Dim tsContent As String
Dim ts As TextStream
Dim fso As New FileSystemObject
fileName = GetPrivateProfileString("MyApp", "sub", _
"", value, Len(value), _
App.Path & "\" & "Config.ini")
...
Set ts = fso.OpenTextFile(fileName,1)
tsContent = ts.ReadAll
Response.Write tsContent
...
Path.Combine
會取用幾個檔案路徑做為引數, 然後將這些引數串連起來,以取得完整路徑,接著對該檔案呼叫 read()
或 write()
。 說明文件根據絕對路徑是第一個參數還是其餘參數,說明幾種不同的情況。 假如是第二個或其餘參數為絕對路徑,則 Path.Combine()
將傳回該絕對路徑。 先前的參數將被忽略。 這對程式碼類似以下範例的應用程式來說,深具啟示作用。
// Called with user-controlled data
public static bytes[] getFile(String filename)
{
String imageDir = "\\FILESHARE\images\";
filepath = Path.Combine(imageDir, filename);
return File.ReadAllBytes(filepath);
}
C:\\inetpub\wwwroot\web.config
),控制應用程式傳回哪些檔案。
...
" Add Binary File to
CALL METHOD lr_abap_zip->add
EXPORTING
name = p_ifile
content = lv_bufferx.
" Read Binary File to
CALL METHOD lr_abap_zip->get
EXPORTING
name = p_ifile
IMPORTING
content = lv_bufferx2.
...
Example 1
中,沒有先驗證 p_ifile
,就對此項目中的資料執行讀/寫功能。如果 ZIP 檔案原本是放置在 Unix 機器的 "/tmp/
" 目錄中,ZIP 項目為 "../etc/hosts
",並根據必要的權限執行應用程式,將會覆寫系統 hosts
檔案。此舉轉而會允許從機器將流量送到攻擊者所要的任何位置,例如,返回攻擊者的機器。
public static void UnzipFile(ZipArchive archive, string destDirectory)
{
foreach (var entry in archive.Entries)
{
string file = entry.FullName;
if (!string.IsNullOrEmpty(file))
{
string destFileName = Path.Combine(destDirectory, file);
entry.ExtractToFile(destFileName, true);
}
}
}
Example 1
中,沒有先驗證 entry.FullName
,就對此項目中的資料執行讀/寫作業。如果 Zip 檔案原本是放置在「C:\TEMP
」目錄中,而 Zip 項目名稱包含「..\
片段」,且應用程式是在所需權限下執行,則會任意覆寫系統檔案。
func Unzip(src string, dest string) ([]string, error) {
var filenames []string
r, err := zip.OpenReader(src)
if err != nil {
return filenames, err
}
defer r.Close()
for _, f := range r.File {
// Store filename/path for returning and using later on
fpath := filepath.Join(dest, f.Name)
filenames = append(filenames, fpath)
if f.FileInfo().IsDir() {
// Make Folder
os.MkdirAll(fpath, os.ModePerm)
continue
}
// Make File
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
return filenames, err
}
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return filenames, err
}
rc, err := f.Open()
if err != nil {
return filenames, err
}
_, err = io.Copy(outFile, rc)
// Close the file without defer to close before next iteration of loop
outFile.Close()
rc.Close()
if err != nil {
return filenames, err
}
}
return filenames, nil
}
Example 1
中,沒有先驗證 f.Name
,就對此項目中的資料執行讀/寫功能。如果 Zip 檔案原本是放置在 Unix 機器的「/tmp/
」目錄中,Zip 項目為「../etc/hosts
」,並根據必要的權限執行應用程式,將會覆寫系統 hosts
檔案。此舉轉而會允許從機器將流量送到攻擊者所要的任何位置,例如,返回攻擊者的機器。
private static final int BUFSIZE = 512;
private static final int TOOBIG = 0x640000;
...
public final void unzip(String filename) throws IOException {
FileInputStream fis = new FileInputStream(filename);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry zipEntry = null;
int numOfEntries = 0;
long total = 0;
try {
while ((zipEntry = zis.getNextEntry()) != null) {
byte data[] = new byte[BUFSIZE];
int count = 0;
String outFileName = zipEntry.getName();
if (zipEntry.isDirectory()){
new File(outFileName).mkdir(); //create the new directory
continue;
}
FileOutputStream outFile = new FileOutputStream(outFileName);
BufferedOutputStream dest = new BufferedOutputStream(outFile, BUFSIZE);
//read data from Zip, but do not read huge entries
while (total + BUFSIZE <= TOOBIG && (count = zis.read(data, 0, BUFSIZE)) != -1) {
dest.write(data, 0, count);
total += count;
}
...
}
} finally{
zis.close();
}
}
...
Example 1
中,沒有先驗證 zipEntry.getName()
,就對此項目中的資料執行讀/寫功能。如果 Zip 檔案原本是放置在 Unix 機器的「/tmp/
」目錄中,Zip 項目為「../etc/hosts
」,並根據必要的權限執行應用程式,將會覆寫系統 hosts
檔案。此舉轉而會允許從機器將流量送到攻擊者所要的任何位置,例如,返回攻擊者的機器。
var unzipper = require('unzipper');
var fs = require('fs');
var untrusted_zip = getZipFromRequest();
fs.createReadStream(zipPath).pipe(unzipper.Extract({ path: 'out' }));
ZZArchive* archive = [ZZArchive archiveWithURL:[NSURL fileURLWithPath: zipPath] error:&error];
for (ZZArchiveEntry* entry in archive.entries) {
NSString *fullPath = [NSString stringWithFormat: @"%@/%@", destPath, [entry fileName]];
[[entry newDataWithError:nil] writeToFile:newFullPath atomically:YES];
}
Example 1
中,沒有先驗證 entry.fileName
,就對此項目中的資料執行讀/寫功能。如果 Zip 檔案原本是放置在 iOS 應用程式的「Documents/hot_patches
」目錄中,Zip 項目為「../js/page.js
」,將會覆寫 page.js
檔案。此舉進而會允許攻擊者插入可能會導致執行程式碼的惡意程式碼。
...
$zip = new ZipArchive();
$zip->open("userdefined.zip", ZipArchive::RDONLY);
$zpm = $zip->getNameIndex(0);
$zip->extractTo($zpm);
...
Example 1
中,沒有先驗證 f.Name
,就對此項目中的資料執行讀/寫功能。如果 Zip 檔案位於 Unix 機器的「/tmp/
」目錄中,Zip 項目為「../etc/hosts
」,並根據必要的權限執行應用程式,將會覆寫系統 hosts
檔案。此舉會允許從機器將流量送到攻擊者所要的任何位置,例如,返回攻擊者的機器。
import zipfile
import tarfile
def unzip(archive_name):
zf = zipfile.ZipFile(archive_name)
zf.extractall(".")
zf.close()
def untar(archive_name):
tf = tarfile.TarFile(archive_name)
tf.extractall(".")
tf.close()
範例 2:以下範例會從 Zip 檔案解壓縮檔案,然後以不安全的方式將檔案寫入磁碟。
import better.files._
...
val zipPath: File = getUntrustedZip()
val destinationPath = file"out/dest"
zipPath.unzipTo(destination = destinationPath)
import better.files._
...
val zipPath: File = getUntrustedZip()
val destinationPath = file"out/dest"
zipPath.newZipInputStream.mapEntries( (entry : ZipEntry) => {
entry.extractTo(destinationPath, new FileInputStream(entry.getName))
})
Example 2
中,沒有先驗證 entry.getName
,就對此項目中的資料執行讀/寫功能。如果 Zip 檔案原本是放置在 Unix 機器的「/tmp/
」目錄中,Zip 項目為「../etc/hosts
」,並根據必要的權限執行應用程式,將會覆寫系統 hosts
檔案。此舉轉而會允許從機器將流量送到攻擊者所要的任何位置,例如,返回攻擊者的機器。
let archive = try ZZArchive.init(url: URL(fileURLWithPath: zipPath))
for entry in archive.entries {
let fullPath = URL(fileURLWithPath: destPath + "/" + entry.fileName)
try entry.newData().write(to: fullPath)
}
Example 1
中,沒有先驗證 entry.fileName
,就對此項目中的資料執行讀/寫功能。如果 Zip 檔案原本是放置在 iOS 應用程式的「Documents/hot_patches
」目錄中,Zip 項目為「../js/page.js
」,將會覆寫 page.js
檔案。此舉進而會允許攻擊者插入可能會導致執行程式碼的惡意程式碼。varName
提供惡意值,則呼叫 SetVariable()
時可能會覆寫包括 #first#
在內的任意變數。在此案例中,若包含 JavaScript 的惡意值覆寫 #first#
,此程式會容易受到 Cross-Site Scripting 攻擊。
<cfset first = "User">
<cfscript>
SetVariable(url.varName, url.varValue);
</cfscript>
<cfoutput>
#first#
</cfoutput>
str
提供惡意值,則呼叫 parse_str()
時可能會在目前範圍中覆寫包括 first
在內的任意變數。在此案例中,若包含 JavaScript 的惡意值覆寫 first
,此程式會容易受到 Cross-Site Scripting 攻擊。
<?php
$first="User";
...
$str = $_SERVER['QUERY_STRING'];
parse_str($str);
echo $first;
?>
str
提供惡意值,則呼叫 mb_parse_str()
時可能會覆寫包括 first
在內的任意變數。在此案例中,若包含 JavaScript 的惡意值覆寫 first
,此程式會容易受到 Cross-Site Scripting 攻擊。
<?php
$first="User";
...
$str = $_SERVER['QUERY_STRING'];
mb_parse_str($str);
echo $first;
?>
NSPredicate
可讓攻擊者修改陳述式的意義。NSPredicate
實例指定應如何從諸如 CoreData
持續性儲存系統、陣列和字典等來源中擷取或篩選集合。其查詢語言提供類似於 SQL
的易懂語言來定義搜尋集合所依據的邏輯條件。NSPredicate
如何用作驗證因素。由於使用者可以提供任意 PIN 值,他們將能夠使用萬用字元 (*
) 來略過 PIN 保護。
NSString *pin = [self getPinFromUser];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"pin LIKE %@", pin];
NSPredicate
可讓攻擊者修改陳述式的意義。NSPredicate
實例指定應如何從諸如 CoreData
持續性儲存系統、陣列和字典等來源中擷取或篩選集合。其查詢語言提供類似於 SQL
的易懂語言來定義搜尋集合所依據的邏輯條件。NSPredicate
如何用作驗證因素。由於使用者可以提供任意 PIN 值,他們將能夠使用萬用字元 (*
) 來略過 PIN 保護。
let pin = getPinFromUser();
let predicate = NSPredicate(format: "pin LIKE '\(pin)'", argumentArray: nil)
...
tid = request->get_form_field( 'tid' ).
CALL TRANSACTION tid USING bdcdata MODE 'N'
MESSAGES INTO messtab.
...
APPHOME
,並隨後根據指定目錄的相對路徑來載入原生程式庫。
...
string lib = ConfigurationManager.AppSettings["APPHOME"];
Environment.ExitCode = AppDomain.CurrentDomain.ExecuteAssembly(lib);
...
APPHOME
以指向包含惡意版本 LIBNAME
的不同路徑,來執行具有應用程式較高權限的任意程式碼。因為程式不會驗證從環境中讀取的值,所以如果攻擊者可以控制系統屬性 APPHOME
的值,那麼他們就可以欺騙應用程式去執行惡意程式碼並取得對系統的控制。
...
RegQueryValueEx(hkey, "APPHOME",
0, 0, (BYTE*)home, &size);
char* lib=(char*)malloc(strlen(home)+strlen(INITLIB));
if (lib) {
strcpy(lib,home);
strcat(lib,INITCMD);
LoadLibrary(lib);
}
...
INITLIB
版本的其他路徑,來提高應用程式權限執行任意程式碼。因為程式不會驗證從環境中讀取的參數,所以如果攻擊者能夠控制系統資源 APPHOME
的值,那麼他們就能欺騙應用程式去執行惡意程式碼並取得對系統的控制。liberty.dll
的程式庫,其可在標準的系統目錄中找到。
LoadLibrary("liberty.dll");
liberty.dll
的絕對路徑。如果攻擊者放置了一個名為 liberty.dll
的惡意程式庫,而這個程式庫在搜尋時的排名比真正想要的程式庫還要前面,並且還有方法可以讓程式在其環境中執行而不是在伺服器的環境中,那麼應用程式將會載入此惡意程式庫,而不是您想要的程式庫。因為這種類型的應用程式都有比較高的權限,因此攻擊者提供的 liberty.dll
將會在比較高的權限下執行,有可能會得到對於系統的完全控制。LoadLibrary()
後得出的排序造成的。如果先於系統目錄搜尋目前目錄 (直到最新版本的 Windows 都是如此),並且攻擊者可能在本地執行程式,則這種類型的攻擊就會變得微不足道。搜尋的順序是依作業系統版本而定,在比較新的系統上,此順序是由此登錄金鑰所控制:
HKLM\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
LoadLibrary()
按以下方式執行:SafeDllSearchMode
為 1,搜尋順序如下:PATH
環境變數中列出來的目錄。SafeDllSearchMode
為 0,搜尋順序如下:PATH
環境變數中列出來的目錄。
...
ACCEPT PROGNAME.
EXEC CICS
LINK PROGRAM(PROGNAME)
COMMAREA(COMA)
LENGTH(LENA)
DATALENGTH(LENI)
SYSID('CONX')
END-EXEC.
...
APPHOME
來決定目錄的安裝位置,並且根據指定目錄的相對路徑來載入原生程式庫。
...
String home = System.getProperty("APPHOME");
String lib = home + LIBNAME;
java.lang.Runtime.getRuntime().load(lib);
...
APPHOME
來指向另一個包含惡意版本 LIBNAME
的路徑,來執行具有應用程式較高權限的任意程式碼。因為程式不會驗證從環境中讀取的值,所以如果攻擊者可以控制系統屬性 APPHOME
的值,那麼他們就可以欺騙應用程式去執行惡意程式碼並取得對系統的控制。 System.loadLibrary()
,從名為 library.dll
的原生程式庫中載入程式碼。通常可以在標準的系統目錄中找到這個程式庫。
...
System.loadLibrary("library.dll");
...
System.loadLibrary()
只接受程式庫名稱,而非路徑。根據 Java 1.4.2 API 說明文件,這個函數運作方式如下 [1]:library.dll
搜索順序放在應用程式想要載入的檔案搜尋順序之前,那麼應用程式就會載入那份惡意程式碼,而不是原來想要載入的檔案。因為應用程式本質的關係,所以應用程式必須有較高的權限才可執行,這意味著攻擊者的 library.dll
內容現在會使用這些權限進行運作,這可能會導致系統完全受到攻擊者的控制。Express
的目前未記錄的「功能」來動態載入程式庫檔案。然後,Node.js
將繼續透過其一般程式庫載入路徑搜尋包含此程式庫的檔案或目錄[1]。
var express = require('express');
var app = express();
app.get('/', function(req, res, next) {
res.render('tutorial/' + req.params.page);
});
Express
中,傳遞至 Response.render()
的頁面將在先前未知時載入延伸的程式庫。這通常適用於「foo.pug」等輸入,因為這意味著載入 pug
程式庫 (已知的範本引擎)。但是,如果攻擊者能夠控制頁面,進而控制延伸,則他們可以選擇在 Node.js
模組載入路徑內載入任何程式庫。因為程式不會驗證從 URL 參數接收的資訊,所以攻擊者可以欺騙應用程式去執行惡意程式碼,並取得對系統的控制。APPHOME
來決定目錄的安裝位置,並且根據指定目錄的相對路徑來載入原生程式庫。
...
$home = getenv("APPHOME");
$lib = $home + $LIBNAME;
dl($lib);
...
APPHOME
來指向另一個包含惡意版本 LIBNAME
的路徑,來執行具有應用程式較高權限的任意程式碼。因為程式不會驗證從環境中讀取的值,所以如果攻擊者可以控制系統屬性 APPHOME
的值,那麼他們就可以欺騙應用程式去執行惡意程式碼並取得對系統的控制。dl()
從程式庫 sockets.dll
載入程式碼,根據您的安裝和配置,該程式碼可從不同的位置載入。
...
dl("sockets");
...
dl()
只接受程式庫名稱,而非路徑。sockets.dll
搜索順序放在應用程式想要載入的檔案搜尋順序之前,那麼應用程式就會載入那份惡意程式碼,而不是原來想要載入的檔案。因為應用程式本質的關係,所以應用程式必須有較高的權限才可執行,這意味著攻擊者的 sockets.dll
內容現在會使用這些權限進行運作,這可能會導致系統完全受到攻擊者的控制。Kernel.system()
以執行通常可在標準系統目錄內找到的名為 program.exe
的執行檔。
...
system("program.exe")
...
Kernel.system()
透過 Shell 執行某些作業。如果攻擊者可操縱環境變數 RUBYSHELL
或 COMSPEC
,則他們可以指向將使用給予 Kernel.system()
的指令呼叫的惡意執行檔。由於應用程式的特性,它需要特定的權限才能執行系統作業,這表示攻擊者的 program.exe
將會在這些權限下執行,攻擊者可能會完全控制系統。Kernel.system()
呼叫前清除其環境。如果攻擊者可以修改 $PATH
變數以指向名為 program.exe
的惡意二位元程式,並讓程式在指定的環境下執行,那麼將會載入惡意二位元程式,而非原先想要載入的程式。由於應用程式的特性,它需要特定的權限才能執行系統作業,這表示攻擊者的 program.exe
將會在這些權限下執行,攻擊者可能會完全控制系統。InvokerServlet
類別允許攻擊者呼叫伺服器的所有類別。InvokerServlet
類別來呼叫伺服器虛擬機中可用的所有類別。藉由猜測類別的完整名稱,攻擊者不僅能載入 Servlet 類別,還可以載入 POJO 類別或 JVM 可用的任何其他類別。
@GetMapping("/prompt_injection")
String generation(String userInput1, ...) {
return this.clientBuilder.build().prompt()
.system(userInput1)
.user(...)
.call()
.content();
}
client = new Anthropic();
# Simulated attacker's input attempting to inject a malicious system prompt
attacker_input = ...
response = client.messages.create(
model = "claude-3-5-sonnet-20240620",
max_tokens=2048,
system = attacker_input,
messages = [
{"role": "user", "content": "Analyze this dataset for anomalies: ..."}
]
);
...
client = OpenAI()
# Simulated attacker's input attempting to inject a malicious system prompt
attacker_input = ...
completion = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": attacker_input},
{"role": "user", "content": "Compose a poem that explains the concept of recursion in programming."}
]
)
@GetMapping("/prompt_injection_persistent")
String generation(String userInput1, ...) {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users WHERE ...");
String userName = "";
if (rs != null) {
rs.next();
userName = rs.getString("userName");
}
return this.clientBuilder.build().prompt()
.system("Assist the user " + userName)
.user(userInput1)
.call()
.content();
}
client = new Anthropic();
# Simulated attacker's input attempting to inject a malicious system prompt
attacker_query = ...;
attacker_name = db.qyery('SELECT name FROM user_profiles WHERE ...');
response = client.messages.create(
model = "claude-3-5-sonnet-20240620",
max_tokens=2048,
system = "Provide assistance to the user " + attacker_name,
messages = [
{"role": "user", "content": attacker_query}
]
);
...
client = OpenAI()
# Simulated attacker's input attempting to inject a malicious system prompt
attacker_name = cursor.fetchone()['name']
attacker_query = ...
completion = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "Provide assistance to the user " + attacker_name},
{"role": "user", "content": attacker_query}
]
)
Object.prototype
的原型,因此如果攻擊者可以覆寫物件的原型,通常就可以覆寫 Object.prototype
的定義,進而影響應用程式內的所有物件。undefined
而不是始終明確設定的屬性,那麼如果原型已受污染,應用程式就可能在無意間讀取原型而不是預期的物件。lodash
版本來污染物件的原型:
import * as lodash from 'lodash'
...
let clonedObject = lodash.merge({}, JSON.parse(untrustedInput));
...
{"__proto__": { "isAdmin": true}}
,則 Object.prototype
將擁有定義的 isAdmin = true
。
...
let config = {}
if (isAuthorizedAsAdmin()){
config.isAdmin = true;
}
...
if (config.isAdmin) {
// do something as the admin
}
...
isAdmin
僅應在 isAuthorizedAdmin()
傳回 true 時才設為 true,但因為應用程式未能在 else 條件中設定 config.isAdmin = false
,所以還是會仰賴 config.isAdmin === undefined === false
的事實。config
的原型現已設定 isAdmin === true
,這會允許略過管理員授權。