Narek Babajanyan
Posted on May 23, 2021
Նախորդ գրառման մեջ տեսանք SQL Injection խոցելիության ընդհանուր բնութագիրը։ Այժմ փորձենք OWASP Juice Shop խոցելի վեբ֊ծրագրում փնտրել տվյալ տեսակի խոցելիություններ։
Ծրագիրը արխիվի տեսքով կարելի է ներբեռնել նախագծի GitHub էջից, այնուհետև npm start
հրամանի միջոցով գործարկել այն (նախապես npm install
հրամանով տեղադրելով ծրագրի պահանջված գրադարանները)։ Գործարկվելիս, Juice Shop֊ը հասանելի է դառնում localhost:3000
հասցեով։
1) Log in with the administrator's user account.
Առաջին հերթին կարելի է ուշադրություն դարձնել Account հղմամբ գտնվող մուտք գործման (login) հատվածին և այստեղ փորձել SQL injection֊ի պարզագույն տարբերակ՝
email: ' OR 1=1 --
password: test
Ստանում ենք հաջողված մուտք ադմինիստրատորի օգտահաշվով, այսինքն տվյալ ծրագրի login էջի email դաշտը ենթակա է SQL injection հարձակման։
Փորձենք հասկանալ ինչու է առաջանում տվյալ խոցելիությունը։ Juice Shop ծրագրի Score board էջում ներկայացված են բոլոր խոցելիությունները, և ամփոված է Ձեր կողմից հայտնաբերված խոցելիությունների քանակը և տեսակները։ Որոշ խոցելիությունների պարագայում, ծրագիրն առաջարկում է կարդալ կոդի այն հատվածը, որտեղ այն առաջանում է, նման կերպ այն կանխել սովորեցնելու նպատակով։
Մենք հենց նոր լուծեցինք "Log in with the administrator's user account" խնդիրը։ Համապատասխան տողի վերջին կոճակը ցույց է տալիս խոցելի կոդի հատվածը, այստեղ տեսնում ենք ինչպես է ծրագրում կազմվում SQL հարցումը՝
SELECT * FROM Users WHERE email = '${req.body.email || ''}' AND password = '${security.hash(req.body.password || '')}' AND deletedAt IS NULL
Այստեղ ${req.body.email || ''}
և ${security.hash(req.body.password || '')}
հանդիսանում են Node.JS պլատֆորմի համար գրված կոդ, որը HTTP հարցումից ստանում է էլ․ փոստը և գաղտնաբառը (գաղտնաբառը նաև անցնում է գաղտնագրման հեշ ֆունկցիայով), որով պետք է մուտք գործել։ Արդյունքում ստացված տվյալներն առանց որևէ ստուգման կամ ֆիլտրման հայտնվում են SQL հարցման մեջ, ինչն էլ հանդիսանում է SQL injection խոցելիության հիմնական պատճառը։
Հետևաբար, երբ email դաշտում մուտագրում ենք ' OR 1=1 --
տողը, ընդհանուր SQL հարցումը ստանում է տվյալ տեսքը՝
SELECT * FROM Users WHERE email = '' OR 1=1 -- ' AND password '${security.hash(req.body.password || '')}' AND deletedAt IS NULL
Տվյալ հարցումն, ըստ էության, վերցնում է տվյալների բազայում պատահած առաջին օգտահաշիվը, առանց էլ․ փոստի և գաղտնաբառի ստուգման (քանի որ գաղտնաբառի ստուգման հատվածն այլևս մեկնաբանություն է և հետևաբար առհամարվում է)։
2) Exfiltrate the entire DB schema definition via SQL Injection.
Հաջորդ "մարտահրավերը" կայանում է տվյալների բազայի կառուցվածքը SQL Injection֊ի միջոցով բացահայտելու մեջ։
Մինչ այդ, սակայն, ուսումնասիրենք SQL Injection կիրառման հայտնի ձևերից մեկը, որը կոչվում է UNION SELECT հարձակում
UNION SELECT Injection
SQL լեզվում UNION
հրամանը վերադարձնում է երկու աղյուսակների միավորումը։ Օգտագործման օրինակին կարելի է ծանոթանալ այստեղ
SQL Injection հարձակումների ենթատեքստում UNION
հրամանն օգտագործվում են տվյալների մեկ աղբյուրի միջոցով, մեկ այլ աղյուսակից տվյալներ ստանալու նպատակով։
Այսինքն մեզ առաջին հերթին անհրաժեշտ է այնպիսի դաշտ, որը ենթակա է SQL Injection հարձակման և որի հարցման արդյունքները տեսանելի են մեզ համար։ Juice Shop ծրագրի պարագայում, այդպիսի ամենահավանական դաշտը ապրանքների որոնման դաշտն է։ Օգտագործելով OWASP ZAP պրոքսի գործիքը (հարցումները և պատասխանները մանրամասն ուսումնասիրելու համար), տեսնում ենք, որ որոնման դաշտում որևէ բառ մուտքագրելիս, հարցում է ուղարկվում http://localhost:3000/rest/products/search
հասցեին, իսկ մուտքագրված արտահայտությունն ուղարկվում է q
պարամետրի տեսքով։
Որոնված արտահայտություն՝ apple
Հարցման հասցե՝ http://localhost:3000/rest/products/search?q=apple
Փորձենք տեղադրել արդեն իսկ քաջ ծանոթ ' OR 1=1 --
արտահայտությունը՝ http://localhost:3000/rest/products/search?q=' OR 1=1 --
հասցեով ուղարկելիս ստանում ենք 500 SequelizeDatabaseError: SQLITE_ERROR: incomplete input տեքստով սխալ։ Թվում է թե օգտակար ոչինչ չստացանք, սակայն ուշադրություն դարձնելով տրված սխալի տեքստին՝ հասկանում ենք, որ գործ ունենք SQLite տեսակի տվյալների բազայի հետ (SQL ընտանիքի այլ տվյալների բազաներից են MySQL֊ը, Oracle֊ը, MSSQL֊ը), տեսնենք թե ինչպես կարող է այս տեղեկատվությունը մեզ օգտակար լինել։
Տվյալների բազաների կառուցվածք
SQL տվյալների բազաներն իրենց մեջ պահում են հատուկ աղյուսակ, որում գրանցվում են տվյալ բազայում տեղ գտած աղյուսակների մասին տվյալներ՝ անվանումները, տեսակները, աղյուսակի ստեղծման համար օգտագործված SQL կոդը և այլն։
Քանի որ արդեն գիտենք, որ Juice Shop ծրագիրն աշխատում է SQLite բազայի հետ, փնտրում ենք մանրամասն տեղեկատվություն այս տեսակի բազաներում կառուցվածքային տվյալների պահման մասին։ Ինչպես երևում է SQLite կայքում զետեղված փաստաթղթից, կառուցվածքային տեղեկատվությունը պահվում է sqlite_schema կոչվող աղյուսակում, որը պարունակում է թվով 5 սյունակ։
Ստացվում է, որ այս խնդիրը լուծելու համար անհրաժեշտ է կատարել UNION SELECT
հարցում և միավորել ապրանքների որոնման և sqlite_schema աղյուսակին արված հարցումների արդյունքները։
Հիշեցնեմ, որ UNION SELECT
հարցում կատարելիս, միավորվող կողմերի աղյուսակները պետք է ունենան հավասար քանակի և նույն տիպերի սյունակներ։ Ապրանքների որոնման դաշտում հարցում կատարելիս տեսնում ենք, որ վերադարձված տվյալներն ունեն թվով 9 սյունակ։ Այս տարբերությունը կարելի է հարթել, պակաս սյունակներով կողմում դատարկ սյունակներ ավելացնելով (կամ թվերի, կամ null
արժեքի տեսքով)։ Փորձենք որոնման q
պարամետրի տեսքով ուղարկել հետևյալ արտահայտությունը՝
' AND 1=2 UNION SELECT *, 6, 7, 8, 9 FROM sqlite_schema --
Այստեղ AND 1=2
արտահայտության իմաստը կայանում է նրանում, որ երբեք չբավարարվող պայմանի միջոցով բացառենք ապրանքների աղյուսակից տվյալների ստացումը և կենտրոնանանք բացառապես sqlite_schema
աղյուսակի արդյունքների վրա։
Այս հարցումը նույնպես վերադաձնում է սխալ, ասելով որ այն թերի է կազմված։ Բարեբախտաբար, Score Board էջում առաջարկվում է տեսնել տվյալ խնդրի համար պատասխանատու կոդը և դրա միջոցով կարող ենք տեսնել թե ինչ SQL հրամաններ են կատարվում ապրանքների որոնման ժամանակ։ Կոդում զետեղված հրամանն ունի հետևյալ տեսքը՝
SELECT * FROM Products WHERE ((name LIKE '%${criteria}%' OR description LIKE '%${criteria}%') AND deletedAt IS NULL) ORDER BY name
, որտեղ criteria
փոփոխականը մեր կողմից մուտքագրված արտահայտությունն է (օրինակ՝ apple juice)։
Ուշադրություն դարձնենք (( բացված փակագծերին, քանի որ դրանք էին մեր հարցման սխալի հիմնական պատճառը։ Հաշվի առնելով սա, մի փոքր փոփոխում ենք մեր ուղարկված արտահայտությունը՝
' AND 1=2)) UNION SELECT *, 6, 7, 8, 9 FROM sqlite_schema --
Այս դեպքում հարցման ամբողջական հասցեն կդառնա՝
localhost:3000/rest/products/search?q=' AND 1=2)) UNION SELECT *, 6, 7, 8, 9 FROM sqlite_schema --
Եվ, ինչպես սպասելի էր, հարցումն ուղարկելուց հետո, տվյալ խնդիրը համարվում է լուծված և ստանում ենք տվյալների բազայի կառուցվածքային մանրամասները։
3) Retrieve a list of all user credentials via SQL Injection
Այս խնդիրը մեզանից պահանջում է հայտնաբերել բոլոր օգտատերերի տվյալները (էլ․փոստ, գաղտնաբառ և այլն)։
Նախորդ խնդիրը լուծելիս ստացանք տվյալների բազայի բոլոր աղյուսակների անունները և կազմավորման կոդը։ Ուսումնասիրելով ստացվածը, տեսնում ենք, որ ունենք Users
կոչվող աղյուսակ, որը կազմվել է հետյալ SQL հրամանով՝
CREATE TABLE `Users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `username` VARCHAR(255) DEFAULT '', `email` VARCHAR(255) UNIQUE, `password` VARCHAR(255), `role` VARCHAR(255) DEFAULT 'customer', `deluxeToken` VARCHAR(255) DEFAULT '', `lastLoginIp` VARCHAR(255) DEFAULT '0.0.0.0', `profileImage` VARCHAR(255) DEFAULT '/assets/public/images/uploads/default.svg', `totpSecret` VARCHAR(255) DEFAULT '', `isActive` TINYINT(1) DEFAULT 1, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `deletedAt` DATETIME)
Այս հրամանն ուսումնասիրելով կարելի է տեսնել թե ինչ սյունակներ է պարունակում Users
աղյուսակը։ Մեզ հատկապես հետաքրքրում են email
և password
սյունակները։
Հետևելով նախորդ խնդրի օրինակին, կրկին դիմում ենք UNION SELECT
հարձակման օգնությանը, այս անգամ միավորելով ապրանքների որոնման հարցումը Users
աղյուսակին կատարված հարցման հետ։
Հիշեցնեմ, որ ապրանքների աղյուսակը պարունակում է թվով 9 սյունակ, իսկ Users
֊ից մեզ անհրաժեշտ են ընդամենը երկուսը։ Այդ պատճառով, SELECT
հրամանը կազմում ենք հետևյալ կերպ՝
SELECT 1,email,password,4,5,6,7,8,9 FROM Users
Թվական արժեքների միջոցով սյունակների ընդհանուր քանակը հասցնում ենք 9֊ի, իսկ email
և password
սյունակները տեղադրում ենք այնպես, որ ապրանքների աղյուսակի տվյալ սյունակներն ունենան նույն տիպը (այս դեպքում՝ text
տիպը)։ Ուսումնասիրելով ապրանքների որոնման արդյունքները, տեսնում ենք, որ երկրորդ և երրորդ սյունակները պարունակում են տեքստային տվյալներ, հետևաբար կարող են միավորվել email
և password
սյուների հետ։
Ընդհանուր հարցումը կունենա հետևյալ տեսքը՝
localhost:3000/rest/products/search?q=' AND 1=2)) UNION SELECT 1,email,password,4,5,6,7,8,9 FROM Users --
Խնդիրը լուծված է։
Օգտակար հղումներ
1) CyHub Armenia֊ի և Վահագն Վարդանյանի "Ծրագրային անվտանգության հիմունքներ" հայերեն դասընթացը
2) SQL injection թեման PortSwigger Web Security Academy֊ում
3) SQL injection թեման Kontra Application Security Training կայքում
Posted on May 23, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.