...
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
に設定され、これにより、管理者による承認をすり抜けることが可能になります。select()
クエリを動的に構築し、実行します。ユーザーは結果を並び替えるカラムを指定することもできます。アプリケーションがすでに認証されており、このコードセグメントの前に customerID
の値を設定していると仮定します。
...
String customerID = getAuthenticatedCustomerID(customerName, customerCredentials);
...
AmazonSimpleDBClient sdbc = new AmazonSimpleDBClient(appAWSCredentials);
String query = "select * from invoices where productCategory = '"
+ productCategory + "' and customerID = '"
+ customerID + "' order by '"
+ sortColumn + "' asc";
SelectResult sdbResult = sdbc.select(new SelectRequest(query));
...
select * from invoices
where productCategory = 'Fax Machines'
and customerID = '12345678'
order by 'price' asc
productCategory
および price
に単一引用符が含まれない場合に限ってクエリは正しく動作します。しかし、攻撃者が、"Fax Machines' or productCategory = \"
" という文字列を productCategory
に、文字列 "\" order by 'price
" を sortColumn
に指定すると、クエリは次のようになります。
select * from invoices
where productCategory = 'Fax Machines' or productCategory = "'
and customerID = '12345678'
order by '" order by 'price' asc
select * from invoices
where productCategory = 'Fax Machines'
or productCategory = "' and customerID = '12345678' order by '"
order by 'price' asc
customerID
で必要な認証を回避でき、すべての顧客について 'Fax Machines'
と一致する領収書のレコードを表示できます。customerID
の値を設定していると仮定します。
...
productCategory = this.getIntent().getExtras().getString("productCategory");
sortColumn = this.getIntent().getExtras().getString("sortColumn");
customerID = getAuthenticatedCustomerID(customerName, customerCredentials);
c = invoicesDB.query(Uri.parse(invoices), columns, "productCategory = '" + productCategory + "' and customerID = '" + customerID + "'", null, null, null, "'" + sortColumn + "'asc", null);
...
select * from invoices
where productCategory = 'Fax Machines'
and customerID = '12345678'
order by 'price' asc
productCategory
を連結して、動的に構築されます。そのため、productCategory
および sortColumn
に単一引用符が含まれない場合にのみ、クエリは正しく動作します。攻撃者が、"Fax Machines' or productCategory = \"
" という文字列を productCategory
に、文字列 "\" order by 'price
" を sortColumn
に指定すると、クエリは次のようになります。
select * from invoices
where productCategory = 'Fax Machines' or productCategory = "'
and customerID = '12345678'
order by '" order by 'price' asc
select * from invoices
where productCategory = 'Fax Machines'
or productCategory = "' and customerID = '12345678' order by '"
order by 'price' asc
customerID
で必要な認証を回避でき、すべての顧客について 'Fax Machines'
と一致する領収書のレコードを表示できます。
...
var authenticated = true;
...
database_connect.query('SELECT * FROM users WHERE name == ? AND password = ? LIMIT 1', userNameFromUser, passwordFromUser, function(err, results){
if (!err && results.length > 0){
authenticated = true;
}else{
authenticated = false;
}
});
if (authenticated){
//do something privileged stuff
authenticatedActions();
}else{
sendUnathenticatedMessage();
}
true
に、そうでない場合は false
に設定しています。しかし、コールバックは IO によってブロックされるため、処理が非同期に実行され、if (authenticated)
の確認の後に実行される可能性があります。また、デフォルトが true なので、ユーザーが実際に認証されるかどうかにかかわらず if 文が実行されます。
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/download/" + "app.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
...
public class Box{
public int area;
public static final int width = 10;
public static final Box box = new Box();
public static final int height = (int) (Math.random() * 100);
public Box(){
area = width * height;
}
...
}
...
Example 1
では、width
の値が 10 であるため、開発者は box.area
が 10 の倍数のランダム整数であると予想します。しかし実際は、常にハードコーディングされた 0 の値です。コンパイル時定数で宣言された static final フィールドは最初に初期化され、順番に実行されます。これは、height
はコンパイル時定数ではないため box
の宣言の後に宣言され、コンストラクターはフィールド height
が初期化される前にコールされることを意味します。
...
class Foo{
public static final int f = Bar.b - 1;
...
}
...
class Bar{
public static final int b = Foo.f + 1;
...
}
This example is perhaps easier to identify, but would be dependent on which class is loaded first by the JVM. In this exampleFoo.f
could be either -1 or 0, andBar.b
could be either 0 or 1.
setuid root
をインストールしたプログラムのものです。このプログラムでは、権限が付与されていないユーザーに代わって特定のファイル操作を行い、アクセス チェックを使用して、現在のユーザーが利用できないはずの操作を、root 権限で行わないようにします。このプログラムでは、access()
システム コールを使用して、プログラムを実行している人が指定されたファイルにアクセスする権限を持っているかどうかを確認してから、ファイルを開き、必要な処理を行います。
if (!access(file,W_OK)) {
f = fopen(file,"w+");
operate(f);
...
}
else {
fprintf(stderr,"Unable to open file %s.\n",file);
}
access()
のコールは想定どおりに機能し、プログラムの実行者がファイルに書き込むために必要な権限を持っている場合は 0
を戻し、そうでない場合は -1 を戻します。しかし、access()
と fopen()
はどちらもファイルハンドルではなくファイル名に対して実行されるため、file
変数が fopen()
に渡される際にも、access()
に渡されたときと同じディスク上のファイルを参照している保証はありません。攻撃者が access()
のコール後に file
を別のファイルへのシンボリックリンクに置き換えた場合、そのファイルが本来ならば攻撃者が変更できないファイルであっても、プログラムはファイルを root 権限で操作します。プログラムを操作して本来実行できない操作を実行させることにより、攻撃者は昇格した権限を獲得します。root
権限を持つプログラムに限定されません。本来は攻撃者に許可されていない操作を実行できるアプリケーションはすべて、ターゲットになる可能性があります。
fd = creat(FILE, 0644); /* Create file */
if (fd == -1)
return;
if (chown(FILE, UID, -1) < 0) { /* Change file owner */
...
}
chown()
のコールにより操作されるファイルが creat()
のコールにより作成されたファイルと同じであると仮定していますが、必ずしもそうならない場合があります。chown()
はファイル名に対して操作され、ファイル ハンドルに対しては操作されないため、攻撃者により、攻撃者が所有しないファイルへのリンクにそのファイルが置き換えられる可能性があります。そのような場合、リンクされたファイルの所有権が chown()
のコールによって攻撃者に付与されます。CBL_CHECK_FILE_EXIST
ルーチンを呼び出して、ファイルを作成して必要な操作を実行する前に、ファイルが存在するかどうかを確認します。
CALL "CBL_CHECK_FILE_EXIST" USING
filename
file-details
RETURNING status-code
END-CALL
IF status-code NOT = 0
MOVE 3 to access-mode
MOVE 0 to deny-mode
MOVE 0 to device
CALL "CBL_CREATE_FILE" USING
filename
access-mode
deny-mode
device
file-handle
RETURNING status-code
END-CALL
END-IF
CBL_CHECK_FILE_EXIST
の呼び出しは想定通りに動作し、0 以外の値を返して、ファイルが存在しないことを示します。ただし、CBL_CHECK_FILE_EXIST
と CBL_CREATE_FILE
の両方がファイルのハンドルではなくファイル名に対して操作を行うため、CBL_CHECK_FILE_EXIST
に渡されたときに行ったのと同様に、CBL_CREATE_FILE
に渡されるときに filename
変数が引き続きディスク上の同じファイルを参照するという保証はありません。CBL_CHECK_FILE_EXIST
を呼び出した後に攻撃者が filename
を作成した場合、CBL_CREATE_FILE
の呼び出しは失敗し、プログラムは、実際には攻撃者によって制御されているデータが含まれるにもかかわらず、ファイルが空であると認識してしまいます。root
権限を使用するプログラムに適用される場合があります。また、アクセスチェックを用いて、現在のユーザーが利用できないはずの操作を、root 権限で行わないようにします。プログラムを操作して許可されていない操作を実行させることにより、攻撃者は高い権限を手に入れる可能性があります。java.text.Format
にあるメソッド parse()
および format()
には、あるユーザーが別のユーザーのデータを表示できるという設計上の不備が含まれています。java.text.Format
にあるメソッド parse()
および format()
には、あるユーザーが別のユーザーのデータを表示できるという Race Condition が含まれています。
public class Common {
private static SimpleDateFormat dateFormat;
...
public String format(Date date) {
return dateFormat.format(date);
}
...
final OtherClass dateFormatAccess=new OtherClass();
...
public void function_running_in_thread1(){
System.out.println("Time in thread 1 should be 12/31/69 4:00 PM, found: "+ dateFormatAccess.format(new Date(0)));
}
public void function_running_in_thread2(){
System.out.println("Time in thread 2 should be around 12/29/09 6:26 AM, found: "+ dateFormatAccess.format(new Date(System.currentTimeMillis())));
}
}
format()
の実装にある Race Condition のため、最初のスレッドの日付が2つ目のスレッドの出力に表示されています。open_basedir
設定オプションには、ファイルアクセス Race Condition に対して脆弱になる設計上の不備が含まれているため、攻撃者が File System における Access Control チェックを回避する可能性があります。open_basedir
設定オプションが設定されている場合、php.ini で指定されているディレクトリツリー以外の場所にあるファイルが PHP プログラムによって操作されないようにします。open_basedir
オプションはセキュリティにとって全面的に好ましいものではありますが、その実装においては、状況次第で攻撃者が制限を回避できてしまう Race Condition が悩みの種となります [2]。TOCTOU (time-of-check, time-of-use) Race Condition は、PHP によってアクセス許可がチェックされるタイミングとファイルが開かれるタイミングの間で発生します。他の言語における File System の Race Condition の場合と同様で、この脆弱性により、攻撃者が Access Control チェックに合格するファイルへのシムリンクの代わりに本来であればそのチェックで不合格となる別のシムリンクを配置して、保護されているファイルにアクセスする可能性があります。Windows.Storage.ApplicationData
クラスの RoamingFolder
または RoamingSettings
プロパティを使用します。RoamingFolder
プロパティおよび RoamingSettings
プロパティは、ローミング アプリのデータストアのコンテナを取得します。このコンテナは、複数デバイス間でのデータの共有に使用することができます。ローミング アプリのデータストアに格納されているオブジェクトに対する読み書きにより、危険にさらされるリスクが高まります。その対象は、ローミング アプリのデータストアを使用してこれらのオブジェクトを共有するデータ、アプリケーション、およびシステムの機密性、完全性、および可用性です。free()
を同じ値で 2 回コールすると、Buffer Overflow が発生する可能性があります。プログラムが free()
を同じ引数で 2 回コールすると、プログラムのメモリ管理データ構造が破損します。これにより、プログラムがクラッシュするか、一部の環境では、その後に malloc()
を 2 回コールして同じポインターを戻すことになります。malloc()
が同じ値を 2 回戻し、その後に攻撃者が、この二重に割り当てられたメモリに書き込まれたデータの制御をプログラムから得た場合、プログラムは Buffer Overflow 攻撃に対して脆弱になります。
void sh(int dummy) {
...
free(global2);
free(global1);
...
}
int main(int argc,char* argv[]) {
...
signal(SIGHUP,sh);
signal(SIGTERM,sh);
...
}
public class GuestBook extends HttpServlet {
String name;
protected void doPost (HttpServletRequest req, HttpServletResponse res) {
name = req.getParameter("name");
...
out.println(name + ", thanks for visiting!");
}
}
Dick
" to name
Jane
" to name
Jane, thanks for visiting!
"Jane, thanks for visiting!
"
public class ConnectionManager {
private static Connection conn = initDbConn();
...
}
dangerouslySetInnerHTML
属性は、不必要にコードから HTML を設定します。dangerouslySetInnerHTML
属性は、ブラウザー DOM での innerHTML の使用に代わるものです。API 名は、これを使用することによる潜在的な危険性を伝えるために変更されています。一般に、コードから HTML を設定することは危険です。ユーザーを誤って Cross-Site Scripting (XSS) 攻撃にさらす可能性が高いためです。dangerouslySetInnerHTML
属性に設定します。
function MyComponent(data) {
return (
<div
dangerouslySetInnerHTML={{__html: data.innerHTML}}
/>
);
}