TL; DR
Maven Central-ის არტეფაქტი, რომელიც გამოქვეყნდა როგორც io.github.davidtimur:c2-lab გამოშვებული იქნა ცხრა ვერსია, რომელთაგან რვამ შეასრულა დისტანციური წვდომის ფუნქცია ნებისმიერი შემდგომი პროექტის კომპილაციის დროს, რომელიც jar ფაილს ანოტაციების დამუშავების გზაზე ათავსებდა. მისი იმპორტისთვის აპლიკაციის კოდი არ იყო საჭირო. წყაროს არცერთი ხაზი არ იყო საჭირო მასზე მითითებისთვის. არ არსებობდა ინსტალაციის სკრიპტი, ინსტალაციის შემდგომი ექვივალენტი და არანაირი სასიცოცხლო ციკლის კაუჭი ინსტრუმენტების შესამოწმებლად.
შესრულების ვექტორი არის ერთი 46-ბაიტიანი ფაილი jar-ში: Java სერვისის პროვაიდერის რეგისტრაცია, რომელიც ასახელებს კლასს, რომელიც ახორციელებს javax.annotation.processing.ProcessorJava კომპილატორი ასეთ რეგისტრაციებს ავტომატურად აღმოაჩენს. მათი პოვნის შემდეგ, javac კომპილაციის ნორმალური ნაწილის სახით ქმნის და მართავს კლასს. ეს ნიშნავს, რომ დატვირთვის გაშვების გარემო არის შემქმნელი მანქანა, იმ მომენტში, როდესაც შემქმნელი მანქანა აკეთებს ერთადერთ რამეს, რისთვისაც არსებობს.
ცხრა ვერსიაში ბრძანებისა და კონტროლის არხი სამჯერ იქნა აღდგენილი: ოპერატორის მიერ მოწოდებული უკუკავშირის URL, შემდეგ ngrok TCP გვირაბის მეშვეობით შებრუნებული გარსი და შემდეგ HTTP გამოკითხვის არხი, რომლის მარშრუტებიც კიდევ ორჯერ შეიცვალა. საბოლოო ვერსიაში JVM-ის ნაგულისხმევ პარამეტრად დამონტაჟდა უოპერაციო TLS ნდობის მენეჯერი. კომპილაციის მთელი პერიოდის განმავლობაში ნებისმიერი სერტიფიკატის მიღება.
არტეფაქტს საკუთარ POM-სა და MIT ლიცენზიაში ეწერა „C2 Lab Payload“. ის Maven Central-ზე მთელი იმ პერიოდის განმავლობაში იყო ხელმისაწვდომი, როდესაც მას ვაკვირდებოდით. მას შემდეგ ამოღებულია, მთლიანად io.github.davidtimur ჯგუფი.
ანატომია: კომპილატორი, როგორც შესრულების მექანიზმი
Java-ს ანოტაციების დამუშავების ჩარჩო არსებობს, რათა ბიბლიოთეკებს შეეძლოთ კოდის გენერირება კომპილაციის დროს — მექანიზმი, რომელიც Lombok-ის, Dagger-ის და ORM-ისა და სერიალიზაციის ხელსაწყოების გრძელი სიის უკან დგას. პროცესორი აცხადებს თავის თავს jar-ში არსებული უბრალო ტექსტური ფაილით:
META-INF/services/javax.annotation.processing.Processor
ამ ფაილის შინაარსი ყველა დაზიანებულ ვერსიაში, სიტყვასიტყვით და სრულად:
io.github.davidtimur.c2lab.C2პროცესორი
ფაილი ბაიტების მიხედვით იდენტურია 1.0.1-დან 1.0.8-მდე ვერსიებში, md5 7d2a08a5c8869a47eea9fa62487dfbe4. ვერსია 1.0.0 მას არ შეიცავს.
როდესაც ჯავაკი გაშვებისას, ის სკანირებს ანოტაციის პროცესორის გზას ამ სერვისის ჩანაწერების მოსაძებნად და ატვირთავს ნაპოვნი ინფორმაციას. კომპილირებულ პროექტში არაფერი საჭიროებს პროცესორის ხსენებას, რაიმეს ანოტირებას ან რაიმეს კონფიგურაციას. გზაზე ყოფნა საკმარისია. ეს არის თვისება, რომელიც განასხვავებს JavacDoor-ს მიწოდების ჯაჭვის ნიმუშებისგან, რომლებზეც აგებულია ინსტრუმენტების უმეტესობა:
| Pattern | Trigger | ხილული როგორც |
|---|---|---|
| npm ინსტალაციის კაუჭი | npm install | scripts.postinstall მანიფესტში |
| Python-ის იმპორტის დროის დატვირთვა | მოდულის პირველი იმპორტი | მოდულის დონის ოპერატორი წყაროში |
| JavacDoor | javac ნებისმიერ ქვედა პროექტზე | სერვისის რეგისტრაციის ფაილის სახელი |
ინსტალაციის კაუჭი მანიფესტში დეკლარაციაა, ხოლო მანიფესტი პირველია, რასაც ვინმე კითხულობს. იმპორტის დროს დატვირთვა, სულ მცირე, წაკითხვად წყაროშია განთავსებული. სერვისის რეგისტრაცია არც ერთი არ არის: ეს არის ფაილის სახელი პლუს ერთი ხაზი, რომელიც კლასის სახელს ასახელებს და ქცევა კომპილირებულ ბაიტკოდშია, რომელიც დირექტორიაში მდებარეობს.
დატვირთვის კლასი თავად ახორციელებს მასპინძლის დაზვერვას გაშვებით. კომპილირებული კლასების მუდმივი პულები შეიცავს /bin/sh, ვინ ვარ მე, uname-a, pwd Unix-ის გზაზე და დავალებების სია Windows-ის გზაზე, ერთად გადამისამართებისშეცდომისნაკადი შვილობილი პროცესის შეცდომის გამომავალი მონაცემების ჩაწერილ ნაკადში შესაერთებლად. ორი JSON შაბლონი შედეგებს ჰოსტიდან - რეგისტრაციის შუქურიდან იღებს:
{"host":"%s","os":"%s","user":"%s","dir":"%s"}
დასახლებული მასპინძლის სახელი ბრძანება და os.name, მომხმარებლის სახელიდა მომხმარებლის.დირექტორი სისტემის თვისებები და შედეგის უკუგამოძახება:
{"version":"%s","host":"%s","time":"%s","output":"%s"}
კომპილატორის მიერ გამომავალში დატოვებული პროგრესის მარკერები უჩვეულოდ აშკარაა: [C2] კომპილაციის დროს შესრულება დასრულდა, [C2] უკუკავშირი გაიგზავნა → HTTP , [C2] გარსი დაკავშირებულია .
ცხრა გამოშვება, სამი C2 თაობა
გამოშვებები არ წარმოადგენს ერთი დატვირთვის ცხრა ასლს. ისინი იტერაციის ჟურნალს წარმოადგენენ და მათი თანმიმდევრობით წაკითხვა აჩვენებს, რომ არხი ხელახლა იქმნება, ხოლო მიწოდების ვექტორი უცვლელი რჩება.
| რელიზი | ავტომატურად სრულდება კომპილაციისას | არხი |
|---|---|---|
1.0.0 | არა (სერვისის ფაილი არ არის) | მხოლოდ ოპერატორის მიერ მოწოდებული უკუკავშირის URL |
1.0.1 | კი (ვექტორი შემოტანილია) | ოპერატორის მიერ მოწოდებული უკუკავშირის URL |
1.0.2 | დიახ | უკუ გარსი, ngrok TCP გვირაბი |
1.0.3 | დიახ | HTTP არხი, /register /cmd /out |
1.0.4 | დიახ | HTTP არხი, /register /cmd /out |
1.0.5 | დიახ | HTTP არხი, /register /poll /out |
1.0.6 | დიახ | HTTP არხი, /register /poll /out |
1.0.7 | დიახ | HTTP არხი, /register /cmd |
1.0.8 | დიახ | HTTP არხი + TLS ვალიდაცია გამორთულია |
ამ ცხრილში სამი დეტალის გამოკვეთა ღირს, რადგან თითოეული მათგანი ცვლის არტეფაქტების ნაკრების წაკითხვის წესს.
ვექტორი 1.0.1-ზე მოდის და არა 1.0.2-ზე. ვერსია 1.0.0 იგივე დაზვერვისა და უკუკავშირის ლოგიკას იყენებს, მაგრამ არ აქვს სერვისის ფაილი და ანოტაციების დამუშავების იმპორტი; ის მხოლოდ მაშინ მუშაობს, თუ რამე გამოიძახებს მას. 1.0.1-დან სერვისის ფაილი არსებობს და კომპილირებული კლასები იმპორტირებულია. javax.annotation.processing.SupportedSourceVersionნებისმიერი შეფასება, რომელიც ადარებს ორ ბილეთიან ვერსიებს — 1.0.0-ს და 1.0.2-ს — სწორად ასკვნის, რომ რაღაც შეიცვალა, მაგრამ არასწორად განსაზღვრავს სად.
საპირისპირო გარსი ზუსტად ერთ ვერსიაში არსებობს. ვერსია 1.0.2 შეიცავს 0.tcp.ngrok[.]io, ჟურნალის ხაზი [C2] გარსი დაკავშირებულია 0.tcp.ngrok[.]io:19823-თან, ინტერაქტიული ბანერი c2-shellდა ფრეიმის ტერმინატორი __დასასრული__1.0.3 ვერსიიდან და შემდგომ ვერსიებში არცერთი მათგანი არ შედის და მათ HTTPS ჰოსტამდე აღწევს. მხოლოდ ბილეთირებული ვერსიების დასახელების შემთხვევაში, გაუქმების მოთხოვნა მიუთითებდა მკვდარ TCP საბოლოო წერტილზე, ხოლო ექვს შემდგომ ვერსიაში არსებულ ცოცხალ HTTP არხს არ მოიხსენიებდა.
საბოლოო ვერსიაში ტრანსპორტირების დადასტურება ამოღებულია. ვერსია 1.0.8-ს ემატება კლასი, რომელიც ახორციელებს javax.net.ssl.X509TrustManager რომლის სერტიფიკატის შემოწმების მეთოდები არაფერს აკეთებს, ყოველთვის ჭეშმარიტი ჰოსტის სახელის ვერიფიკატორი, რომელიც რეგისტრირებულია setDefaultHostnameVerifierდა ყველას ნდობა რუტინა, რომელიც ორივეს JVM-ის ნაგულისხმევ პარამეტრებად აინსტალირებს. შედეგად, კომპილაციის დარჩენილი პერიოდის განმავლობაში, JVM იღებს ნებისმიერ სერტიფიკატს ნებისმიერი ჰოსტიდან — არა მხოლოდ საკუთარი დატვირთვის ტრაფიკისთვის, არამედ ყველაფრისთვის, რასაც ბილდი შემდგომში TLS-ის საშუალებით აკეთებს.
HTTP არხს, ექვსივე რელიზში, რომელიც მას იყენებს, ერთი ჰოსტი მართავს: tableful-fervor-crazed.ngrok-free[.]devყველა მოთხოვნას აქვს სათაური ngrok-skip-browser-warning, რომელიც თრგუნავს ინტერსტიციულ გვერდს, რომელსაც უფასო ngrok გვირაბები ბრაუზერებს აწვდიან. ბილიკების ნაკრები იცვლება ვერსიებს შორის — /გამავალი ქრება 1.0.7-ზე და /cmd მონაცვლეობს /გამოკითხვა — მაგრამ მასპინძელი არასდროს იცვლება.
თვითგამორიცხვის სიგნალი
1.0.1 ვერსიიდან მოყოლებული, ყველა POM არტეფაქტის საკუთარი აწყობისთვის კომპილატორის არგუმენტს ადგენს:
-პროც: არცერთი
ეს დროშა ანოტაციების დამუშავებას გამორთავს. მისი ეფექტი აქ წინასწარ არისcise: როდესაც პროცესორის შემცველი პროექტი თავად არის კომპილირებული, პროცესორი არ მუშაობს.
ამ დროშას სრულიად ჩვეულებრივი გამოყენება აქვს. პროექტს, რომელიც ანოტაციის პროცესორს აწვდის, ხშირად უწევს თავიდან აიცილოს ამ პროცესორის საკუთარ თავზე გამოყენება ჩატვირთვის დროს და build-tool-ის დოკუმენტაცია სწორედ ამას გვირჩევს. ცალკე აღებული, ეს არაფერს ამტკიცებს.
თუმცა, პროცესორის ქმედებებთან ერთად, ეს აღწერს კონკრეტულ ასიმეტრიას: კოდი სრულდება ყველა იმ მანქანის მანქანებზე, ვინც კომპილაციას ახდენს არტეფაქტის წინააღმდეგ და არა იმ მანქანაზე, რომელიც აწყობს არტეფაქტს. დროშა ჩნდება იმავე რელიზში, რომელიც წარმოგიდგენთ სერვისის ფაილს — 1.0.1 — და მის შემდგომ ყველა რელიზში. კორელაცია „რელიზს, სადაც იწყება კომპილაციის დროს შესრულება“ და „რელიზს, სადაც კომპილაციის დროს შესრულება ლოკალურად გამორთულია“ არის არტეფაქტების ნაკრებში ყველაზე სასარგებლო ანალიტიკური სიგნალი და ის ჩანს უბრალო ტექსტურ POM-ში არაფრის დეკომპილაციის გარეშე.
ეფექტს აღვნიშნავთ და ამით ვჩერდებით. არტეფაქტებში არაფერი მიუთითებს იმაზე, თუ რატომ იყო დროშა აღმართული.
კიდევ ერთი მეტამონაცემების ნაწილი, რომელიც ძირითადად მის განადგურებას ეხება, აღსანიშნავია. POM პროექტს „C2 ლაბორატორიის დატვირთვას“ უწოდებს, აღწერს მას, როგორც „C2 ლაბორატორიის დატვირთვის არტეფაქტს“ და MIT-ის ლიცენზიას აძლევს. ამ ტიპის თვითმონიშვნა ზოგჯერ იმის დასტურად არის შემოთავაზებული, რომ პაკეტი კვლევითი სამუშაოა.cisცოცხალი საფრთხის ნაცვლად, და ზოგჯერ ეს წაკითხვა სწორია - დეკლარირებული კანარის სქესი, რომელსაც არ აქვს ხელმისაწვდომი ინფრასტრუქტურა, ამ ობიექტისგან განსხვავებული ობიექტია. ეს აქ არ გამოიყენება. საჯარო საცავში გამოქვეყნებული ფუნქციური იმპლანტი, რომელიც ხელმისაწვდომია ნებისმიერი მომხმარებლისთვის, გამავალი ინფრასტრუქტურით, რომელიც სამჯერ იქნა გადაკეთებული ცხრა ვერსიის განმავლობაში, არის ცოცხალი შესაძლებლობა, მიუხედავად იმისა, თუ როგორ ეწოდება მას მისი მეტამონაცემები. POM-ში სახელი არაფერს ცვლის იმის შესახებ, თუ რა ხდება მის წინააღმდეგ კომპილირებულ მანქანაზე.
ინდიკატორები ასაწყობი მანქანებისთვის
თუ აწყობის ჰოსტი ამ არტეფაქტის მიხედვით კომპილირდება, მტკიცებულება დისკზე მუდმივ იმპლანტში კი არა, აწყობის ჟურნალებსა და ქსელურ ტელემეტრიაში ინახება — დატვირთვა კომპილატორის პროცესში მუშაობს და მასთან ერთად მთავრდება.
jar-ში ან ლოკალურ რეპოზიტორის ქეშში
META-INF/services/javax.annotation.processing.Processorდასახელებისგანio.github.davidtimur.c2lab.C2Processor- სერვისის ფაილი md5
7d2a08a5c8869a47eea9fa62487dfbe4 - კლასები
io/github/davidtimur/c2lab/:C2Processor,C2Task,Task, ხოლო 1.0.8 ვერსიაში შიდა კლასიTask$1
აწყობის პროცესში გამომავალი
[C2] compile-time execution complete[C2] callback sent → HTTP[C2] callback failed:[C2] shell connected to- ინტერაქტიული ბანერი
c2-shell; ფრეიმის ტერმინატორი__END__
პროცესის ტელემეტრია
javacროგორც მშობელი/bin/sh -c(Unix) ან Windows-ის ბრძანებების ინტერპრეტატორი- ბავშვის ბრძანებები
whoami,uname -a,pwd(Unix) ანtasklist(Windows) კომპილაციის ნაბიჯით მშობელი
ქსელურ ტელემეტრიაში
- გამავალი TCP-ის მისამართისთვის
0.tcp.ngrok[.]io:19823(გამოშვება 1.0.2) - HTTPS-დან
tableful-fervor-crazed.ngrok-free[.]dev, ბილიკები/register,/cmd,/poll,/out(გამოცემები 1.0.3-დან 1.0.8-მდე) - მოთხოვნის სათაური
ngrok-skip-browser-warning: true - მოთხოვნის შესაბამისი სხეულები
{"host":...,"os":...,"user":...,"dir":...}or{"version":...,"host":...,"time":...,"output":...}
კონფიგურაციაში
- Გარემოს ცვლადები
CALLBACK,CALLBACK_URLსისტემის თვისებაcallback.url
გამომცემლის მეტამონაცემები
- Group
io.github.davidtimur; გამომცემლის მისამართიdavudboi999@gmail[.]comხელმოწერის გასაღებიE520C345EF94423D
ვერსია 1.0.8-ის გამოყენებით გაშვებული ვერსია დამატებით შემოწმებას მოითხოვს. რადგან ეს ვერსია JVM-ის მასშტაბით ნაგულისხმევ პარამეტრად ნებართვის მქონე ნდობის მენეჯერს აყენებს, ნებისმიერი TLS კავშირი, რომელიც მოგვიანებით განხორციელდა იმავე JVM-ში — დამოკიდებულების მოგვარება, არტეფაქტის ატვირთვა, განლაგების ეტაპი — სერტიფიკატის ვალიდაციის გარეშე მიმდინარეობდა. ამ ფანჯრიდან ტრაფიკი არ უნდა ჩაითვალოს ავთენტიფიკაციად.
რატომ სჭირდებათ კომპილირებულ არტეფაქტებს განსხვავებული სკანირება
JavacDoor სასარგებლო სატესტო შემთხვევაა, რადგან ის ერთდროულად ორ საერთო დაშვებას უარყოფს და არცერთი წარუმატებლობა არ არის სპეციფიკური რომელიმე მომწოდებლის ინსტრუმენტებისთვის.
პირველი ვარაუდი ის არის, რომ საშიში კოდი თავს მანიფესტში აცხადებს. მიწოდების ჯაჭვის ინსტრუმენტების დიდი ნაწილი ორგანიზებულია სასიცოცხლო ციკლის მიხედვით. hooks, რადგან npm-ისა და PyPI-სთვის მოქმედება, როგორც წესი, სწორედ აქ ხდება. JavacDoor-ს არ აქვს კაუჭი. მისი ტრიგერი არის სერვისის რეგისტრაციის ფაილი, რომლის სახელიც Java ინტერფეისია და შინაარსი კი კლასის სახელი. ამის სტატიკურად დასაჭერად, თქვენ უნდა დაამუშაოთ META-INF/services/javax.annotation.processing.Processor როგორც შესრულების შესვლის წერტილი თავისთავად, ისევე როგორც პოსტინსტალაცია სკრიპტი — და შემდეგ მიჰყევით დასახელებულ კლასს ბაიტკოდში. ეკოსისტემებს აქვთ ამ ფორმის საკუთარი ავტომატური აღმოჩენის მექანიზმები და თითოეული მათგანი წარმოადგენს შესვლის წერტილს, მიუხედავად იმისა, ჩამოთვლის თუ არა ის ინსტრუმენტიზაციას.
მეორე ვარაუდი ის არის, რომ სტრიქონები წყაროს ფაილებშია. Jar-ისთვის, საბოლოო წერტილები, shell ბრძანებები, JSON შაბლონები და ჟურნალის მარკერები ყველა მუდმივ პულებშია. .კლასი ფაილები. ტექსტის greps-ის ინსტრუმენტი ვერაფერს პოულობს — არა იმიტომ, რომ სტრიქონები დაბინდულია, არამედ იმიტომ, რომ ისინი სტრუქტურირებულ ბინარულ კონტეინერშია, რომელსაც ტექსტის სკანირება არ აანალიზებს. ამ პოსტში მოცემული ყველა ქსელის ინდიკატორი მუდმივი პულის ანალიზით არის აღებული. ერთ-ერთი მათგანი სრულად ჩამოყალიბებული https:// URL კლასის ფაილში ადვილად ხილვად მდგომარეობაშია; jar-ის წასაკითხი შინაარსის ტექსტური სკანირება მაინც ვერ გამოავლენს მას. აქ კოდირება არ არის გასანეიტრალებელი, მხოლოდ კონტეინერის ფორმატია წასაკითხი.
ორივე ხარვეზს ერთი და იგივე ფორმა აქვს: არტეფაქტის ფორმატი ფაილების ტომრად განიხილებოდა და არა განსაზღვრული სემანტიკის მქონე სტრუქტურად. კორექტირება არასერიოზულია - კონტეინერის გაანალიზება, ეკოსისტემის საკუთარი ავტომატური აღმოჩენის შესვლის წერტილების ჩამოთვლა და მათი კომპილირებულ კოდში გაყოლა. კონკრეტულად აწყობის დროის ვექტორებისთვის, მესამე შემოწმება იაფი და გასაკვირი დიაგნოსტიკურია: შეადარეთ ის, რასაც არტეფაქტი მომხმარებლებისთვის აკეთებს იმასთან, რისგანაც ის თავისუფლდება. არტეფაქტი, რომელიც რეგისტრირებს კომპილაციის დროის პროცესორს და ამავდროულად გამორთავს კომპილაციის დროის დამუშავებას საკუთარი აწყობისთვის, საკუთარ თავზე რაღაცას გითხრათ უბრალო ტექსტური POM-ის ორ სტრიქონში.
დღეს Maven-ის არტეფაქტების მომხმარებელი გუნდებისთვის სამი პრაქტიკული ზომაა საჭირო:
- ანოტაცია-პროცესორის გზა შესრულების საზღვრად მიიჩნიეთ. იქ მიმავალი დამოკიდებულებები თქვენს ბილდში კოდს ამუშავებენ. იმ შემთხვევაში, თუ ბილდს ანოტაციების დამუშავება არ სჭირდება, -პროც: არცერთი თავდაცვითი თვალსაზრისით ისეთივე სასარგებლოა, როგორც, როგორც ჩანს, ლოკალურად იყო აქ; სადაც ეს ხდება, პროცესორის ნაკრების პინირება ხდება ექსპლიციტურად, კომპილაციის კლასის გზიდან მემკვიდრეობით მიღების ნაცვლად.
- ლოგის კომპილატორის ქვეპროცესები. კომპილაციის ნაბიჯი, რომელიც shell-ს წარმოქმნის, უმეტეს პროექტებში ანომალიურია და ტრივიალურად გასაფრთხილებელია.
- მეტამონაცემებს ჩვენებად ნუ მიიჩნევთ. პაკეტის სახელში ან აღწერილობაში „ლაბორატორია“, „ტესტი“, „დატვირთვა“ და „PoC“ არ წარმოადგენს ფარგლების შეზღუდვებს. ხელმისაწვდომობა და ქცევა ფარგლებს ზღუდავს.
არტეფაქტი და მისი მთელი ჯგუფი ამოღებულია Maven ჩვენი ანგარიშის შემდეგ Central; როგორც არტეფაქტის გზა, ასევე ჯგუფის გზა ახლა აბრუნებს 404-ს, ხოლო Central ინდექსი არ აჩვენებს შესაბამის კოორდინატებს. ეს ხურავს ამ არტეფაქტს. ის არ ხურავს ვექტორს, რომელიც Java კომპილატორის დოკუმენტირებული ფუნქციაა და ხელმისაწვდომია ყველასთვის, ვინც jar-ს აქვეყნებს.
ლიტერატურა
ეს პოსტი გარე წყაროებს არ მოიხსენიებს. ყველა აღმოჩენა წარმოადგენს ცხრა გამოქვეყნებული ქილის პირველადი სტატიკურ ანალიზს, რომლებიც ამოღებულია Maven Central-დან მათი ამოღებამდე. არტეფაქტებიდან არცერთი კოდი არ შესრულებულა არცერთ ეტაპზე.







