DEV Community

Cover image for Ծրագրային անվտանգություն՝ SQL Injection (մաս 2)
Narek Babajanyan
Narek Babajanyan

Posted on

Ծրագրային անվտանգություն՝ SQL Injection (մաս 2)

Նախորդ գրառման մեջ տեսանք 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 էջում ներկայացված են բոլոր խոցելիությունները, և ամփոված է Ձեր կողմից հայտնաբերված խոցելիությունների քանակը և տեսակները։ Որոշ խոցելիությունների պարագայում, ծրագիրն առաջարկում է կարդալ կոդի այն հատվածը, որտեղ այն առաջանում է, նման կերպ այն կանխել սովորեցնելու նպատակով։

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
Enter fullscreen mode Exit fullscreen mode

Տվյալ հարցումն, ըստ էության, վերցնում է տվյալների բազայում պատահած առաջին օգտահաշիվը, առանց էլ․ փոստի և գաղտնաբառի ստուգման (քանի որ գաղտնաբառի ստուգման հատվածն այլևս մեկնաբանություն է և հետևաբար առհամարվում է)։

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)
Enter fullscreen mode Exit fullscreen mode

Այս հրամանն ուսումնասիրելով կարելի է տեսնել թե ինչ սյունակներ է պարունակում 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 կայքում

Top comments (0)