בלבול טיפוסים - Type Juggling - תרגול¶
תרגיל 1 - עקיפת אימות עם magic hash (Apprentice)¶
רקע:
לפניכם מערכת כניסה ב-PHP שמשתמשת בהשוואה רופפת של MD5. המטרה: להיכנס ללא ידיעת הסיסמה.
קוד המערכת:
$password = $_POST['password'];
$hash = md5($password);
$stored_hash = $db->getHash($username); // "0e462097431906509019562988736854"
if ($hash == $stored_hash) {
login($username);
}
שלבים:
- שימו לב שה-hash השמור מתחיל ב-
0e - מצאו מחרוזת שה-MD5 שלה גם מתחיל ב-
0eואחריו רק ספרות - השתמשו ברשימת ה-magic hashes המוכרות
- שלחו את המחרוזת כסיסמה
שאלות:
- למה "0e462..." מושווה ל-0 בהשוואה רופפת?
- איזה מחרוזות עובדות כאן?
- האם התקיפה תעבוד ב-PHP 8.0 ומעלה?
תרגיל 2 - עקיפת אימות עם JSON type manipulation¶
רקע:
אפליקציית PHP שמקבלת JSON ומשתמשת בהשוואה רופפת לאימות סיסמה.
קוד המערכת:
$data = json_decode(file_get_contents('php://input'), true);
$user = getUserByUsername($data['username']);
if ($data['password'] == $user['password']) {
createSession($user);
echo json_encode(['success' => true]);
}
שלבים:
- נסו את הבקשה הרגילה:
POST /login HTTP/1.1
Content-Type: application/json
{"username": "admin", "password": "wrongpassword"}
- שנו את טיפוס הסיסמה ל-
true:
- נסו גם עם
0(ב-PHP < 8.0):
- נסו עם מערך ריק:
שאלות:
- איזה payload עבד? למה?
- מה ההבדל בין שליחת true (בוליאני) ל-"true" (מחרוזת)?
תרגיל 3 - עקיפת strcmp¶
רקע:
מערכת אימות שמשתמשת ב-strcmp לבדיקת סיסמה.
קוד המערכת:
$password = $_POST['password'];
$stored = getStoredPassword($username);
if (strcmp($password, $stored) == 0) {
echo "Login successful";
}
שלבים:
- שלחו בקשה רגילה וודאו שהאימות נכשל
- שנו את הפרמטר
passwordלמערך:
POST /login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=admin&password[]=anything
- בדקו אם הכניסה הצליחה
שאלות:
- מה מחזירה strcmp כשהיא מקבלת מערך?
- למה NULL == 0 מחזיר true ב-PHP?
- כתבו את התיקון לקוד הזה
תרגיל 4 - עקיפת בדיקת טוקן¶
רקע:
מערכת איפוס סיסמה שמשתמשת בהשוואה רופפת לבדיקת טוקן.
קוד המערכת:
$token = $_GET['token'];
$user = getUserByEmail($_GET['email']);
if ($token == $user['reset_token']) {
showResetForm();
}
שלבים:
- בקשו איפוס סיסמה עבור המשתמש
admin - נסו לגשת לדף האיפוס עם טוקנים שונים:
/reset?email=admin&token=0
/reset?email=admin&token=true
/reset?email=admin&token[]=
/reset?email=admin&token=0e1234
- בדקו מה עובר את הבדיקה
רמז: אם הטוקן מתחיל ב-0e או מכיל אותיות, יש אפשרויות שונות לעקיפה.
תרגיל 5 - שרשור בלבול טיפוסים עם SQLi¶
רקע:
אפליקציה שמשתמשת ב-is_numeric ו-intval לולידציה.
קוד המערכת:
$id = $_GET['id'];
if (is_numeric($id) && intval($id) > 0) {
$result = $db->query("SELECT * FROM products WHERE id = $id");
displayProduct($result);
}
שלבים:
- שלחו ערך מספרי רגיל וודאו שעובד:
?id=1 - נסו הזרקת SQL רגילה - האם
is_numericחוסמת אותה? - נסו ערכים בפורמטים שונים:
- הקסדצימלי:
?id=0x1 - סימון מדעי:
?id=1e0 - עם רווחים:
?id=%201%20 - בדקו גרסת PHP - האם
is_numeric("0x539")מחזירהtrue? - ב-PHP < 7.0, נסו:
?id=0x31206f722031
רמז: ב-PHP ישנות, ערכים הקסדצימליים נחשבים מספריים ו-intval מחזיר ערך חיובי.
תרגיל 6 - בלבול טיפוסים ב-JavaScript (Node.js)¶
רקע:
שרת Node.js עם Express שמקבל JSON.
קוד המערכת:
app.post('/api/auth', (req, res) => {
const { username, password, isAdmin } = req.body;
const user = users.find(u => u.username === username);
if (!user || user.password !== password) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// פגיע - הערך נלקח מבקשת הלקוח
const token = generateToken({
userId: user.id,
admin: isAdmin || user.isAdmin
});
res.json({ token });
});
שלבים:
- התחברו כמשתמש רגיל ובדקו את הטוקן שקיבלתם
- שלחו את הבקשה עם שדה נוסף:
POST /api/auth HTTP/1.1
Content-Type: application/json
{"username": "user", "password": "pass123", "isAdmin": true}
- בדקו את הטוקן - האם יש לכם הרשאות אדמין?
שאלות:
- למה isAdmin || user.isAdmin פגיע?
- מה ההבדל בין זה לבין mass assignment?
- כתבו תיקון לקוד
תרגיל 7 - ניתוח קוד וזיהוי פגיעויות (Expert)¶
לפניכם קוד PHP. מצאו את כל פגיעויות בלבול הטיפוסים:
function processPayment($data) {
// בדיקה 1
if ($data['amount'] == 0) {
return ['error' => 'Amount cannot be zero'];
}
// בדיקה 2
if (strlen($data['card_number']) == 16) {
// עיבוד כרטיס אשראי
}
// בדיקה 3
if ($data['cvv'] == $stored_cvv) {
// CVV תקין
}
// בדיקה 4
$discount = $data['discount_code'];
if ($discount != false) {
applyDiscount($discount);
}
// בדיקה 5
if (in_array($data['currency'], ['USD', 'EUR', 'GBP'])) {
// מטבע תקף
}
}
שאלות:
1. מצאו לפחות 4 פגיעויות
2. הסבירו כיצד לנצל כל אחת
3. כתבו גרסה מתוקנת של כל בדיקה
4. האם in_array פגיע לבלבול טיפוסים? (רמז: כן, בלי הפרמטר השלישי true)