入力の検証や表現の問題は、メタキャラクター、代替エンコーディング、数値表現などによって引き起こされます。セキュリティの問題は、入力を信頼することに起因します。この問題に含まれるのは、「Buffer Overflow」、「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}
) を使用します。最初の評価の結果を掌握している攻撃者が、2 回目の OGNL 評価で評価される式を制御し、任意の OGNL 式を挿入できるようになる可能性があります。redirectAction
の結果はそのパラメーターを 2 回評価することが知られています。この場合、攻撃者が 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" にリダイレクトされることになります。dest
リクエスト パラメーターからの URL を含む PageReference
オブジェクトを返します。
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" にリダイレクトされることになります。http://
スキームを使用して元の URL をポイントするように requestToLoad
を設定し、さらにこのリクエストを 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()
のコールは、タイプ char
の MAX
要素が含まれている、cArray
の割り当てられた境界外からメモリを読み取ります。一方の 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]
に割り当てられているデータを超えて検索が実行される場合があります。この場合には、x
が argv[0]
で見つかりません。strncpy()
など境界が定められた関数の場合も、不正に使用されると脆弱性の原因になります。メモリの操作と、データのサイズや構成に関する誤った想定が同時に発生することが、大部分の Buffer Overflow の根本的な原因です。char
の配列を連続的に間接参照していますが、最後の参照によって「一つ違い」エラーが発生します。
char Read() {
char buf[5];
return 0
+ buf[0]
+ buf[1]
+ buf[2]
+ buf[3]
+ buf[4]
+ buf[5];
}
strncpy()
など境界が定められた関数の場合も、不正に使用されると脆弱性の原因になります。メモリの操作と、データのサイズや構成に関する誤った想定が同時に発生することが、大部分の Buffer Overflow の根本的な原因です。getInputLength()
から読み取られた信頼できない値が、コピー先バッファである output
のサイズより小さいかどうかをチェックしています。しかし、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
を応用しています。
...
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
を応用しています。
...
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()
を呼び出します。 ドキュメントには、最初のパラメーターまたは残りのパラメーターが絶対パスかどうかに基づいて、いくつかの異なるシナリオが記述されています。 2 番目または残りのパラメーターが絶対パスの場合、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);
...
LIBNAME
が含まれる別のパスを参照するようにアプリケーション構成プロパティ APPHOME
を変更することにより、ライブラリまたは実行可能ファイルをロードして、任意のコードをアプリケーションの上位権限で実行することを攻撃者に許可します。このプログラムは環境から読み取った値の検証を行わないので、システム プロパティ 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
という名前を付けた悪意のあるライブラリを、目的のファイルより優先順位の高い検索場所に配置し、Web サーバー環境ではなく攻撃者の環境でプログラムを実行できる方法を確立すると、アプリケーションによって信頼されたファイルの代わりに悪意のあるライブラリがロードされます。こうしたアプリケーションのタイプは昇格した権限で実行されるため、攻撃者の 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);
...
LIBNAME
が含まれている別のパスを参照するようにシステムプロパティ APPHOME
を変更することにより、攻撃者はライブラリをロードしてアプリケーションの任意のコマンドを昇格された権限で実行できます。このプログラムは環境から読み取った値の検証を行わないので、システムプロパティ 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);
...
LIBNAME
が含まれている別のパスを参照するようにシステムプロパティ APPHOME
を変更することにより、攻撃者はライブラリをロードしてアプリケーションの任意のコマンドを昇格された権限で実行できます。このプログラムは環境から読み取った値の検証を行わないので、システムプロパティ APPHOME
の値を制御できれば、攻撃者はアプリケーションを操作して悪意のあるコードを実行させ、システムを支配下に置くことができます。dl()
を使用して、sockets.dll
という名前のライブラリからコードをロードします。これは、インストールや構成に応じて、さまざまな場所からロードされる可能性があります。
...
dl("sockets");
...
dl()
がロードされるライブラリの、パスではなくライブラリ名を受け取ることです。sockets.dll
の悪意あるコピーを、アプリケーションがロードしようとするファイルより前の検索場所に配置することが可能な場合、アプリケーションは目的のファイルの代わりに悪意あるコピーをロードします。アプリケーションの性質上、このバイナリは昇格された権限で実行されます。つまり、攻撃者の sockets.dll
の内容もその昇格された権限で実行されるため、攻撃者はシステムを完全に制御してしまう可能性があります。Kernel.system()
を実行して、通常は標準システム ディレクトリ内にある program.exe
という実行可能プログラムを実行します。
...
system("program.exe")
...
Kernel.system()
は実行にシェルを使用します。攻撃者が環境変数 RUBYSHELL
または COMSPEC
を操作できる場合、変数から悪意のある実行可能プログラムを参照でき、それが Kernel.system()
へのコマンドでコールされる可能性があります。アプリケーションの性質上、このバイナリはシステム操作の実行に必要な権限で実行されます。つまり、攻撃者の program.exe
もその権限で実行されるため、攻撃者はシステムを完全に制御してしまう可能性があります。Kernel.system()
のコールを実行する前に環境をクリーンにできていないことです。攻撃者が $PATH
変数を変更して、program.exe
という名前の悪意あるバイナリを参照させ、攻撃者の環境でプログラムが実行されるようにすると、本来のバイナリでなく悪意あるバイナリがロードされます。アプリケーションの性質上、このバイナリはシステム操作の実行に必要な権限で実行されます。つまり、攻撃者の program.exe
もその権限で実行されるため、攻撃者はシステムを完全に制御してしまう可能性があります。InvokerServlet
クラスを使用すると、サーバー上の任意のクラスが攻撃者により起動される可能性があります。InvokerServlet
クラスを使用すると、サーバーの仮想マシンで使用可能なクラスが起動できる可能性があります。攻撃者はクラスの完全修飾名を推測することにより、サーブレットクラスだけではなく、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
に設定され、これにより、管理者による承認をすり抜けることが可能になります。