From 2deae38a4ebabc414277b3560d0d824ed1c1ce34 Mon Sep 17 00:00:00 2001 From: evazion Date: Wed, 16 Nov 2022 01:53:50 -0600 Subject: [PATCH] uploads: allow uploading .zip, .rar., and .7z files from disk. Allow uploading .zip, .rar, and .7z files from disk. The archive will be extracted and the images inside will be uploaded. This only works for archive files uploaded from disk, not from a source URL. Post source URLs will look something like this: "file://foo.zip/1.jpg", "file://foo.zip/2.jpg", etc. Sometimes artists uses Shift JIS or other encodings instead of UTF-8 for filenames. In these cases we just assume the filename is UTF-8 and replace invalid characters with '?', so filenames might be wrong in some cases. There are various protections to prevent uploading malicious archive files: * Archives with more than 100 files aren't allowed. * Archives that decompress to more than 100MB aren't allowed. * Archives with filenames containing '..' components aren't allowed (e.g. '../../../../../etc/passwd'). * Archives with filenames containing absolute paths aren't allowed (e.g. '/etc/passwd'). * Archives containing symlinks aren't allowed (e.g. 'foo -> /etc/passwd'). * Archive types other than .zip, .rar, and .7z aren't allowed (e.g. .tar.gz, .cpio). * File permissions, owners, and other metadata are ignored. Partial fix for #5340: Add support for extracting archive attachments from certain sources --- app/logical/danbooru/archive.rb | 34 ++++-- app/models/upload.rb | 114 +++++++++++++++++---- test/files/archive/42.zip | Bin 0 -> 42838 bytes test/files/archive/absolute-path.7z | Bin 0 -> 106 bytes test/files/archive/bomb-1-1G.rar | Bin 0 -> 54452 bytes test/files/archive/bomb-100-10M.rar | Bin 0 -> 61529 bytes test/files/archive/bomb-10k-files.7z | Bin 0 -> 4678 bytes test/files/archive/symlink.zip | Bin 0 -> 173 bytes test/files/archive/ugoira.tar | Bin 0 -> 20480 bytes test/files/archive/ugoira.tar.7z | Bin 5084 -> 0 bytes test/files/archive/ugoira.tar.gz | Bin 0 -> 5151 bytes test/files/archive/zip-slip.zip | Bin 0 -> 545 bytes test/functional/uploads_controller_test.rb | 74 ++++++++++++- test/unit/danbooru_archive_test.rb | 41 +++++++- 14 files changed, 232 insertions(+), 31 deletions(-) create mode 100644 test/files/archive/42.zip create mode 100644 test/files/archive/absolute-path.7z create mode 100644 test/files/archive/bomb-1-1G.rar create mode 100644 test/files/archive/bomb-100-10M.rar create mode 100644 test/files/archive/bomb-10k-files.7z create mode 100644 test/files/archive/symlink.zip create mode 100644 test/files/archive/ugoira.tar delete mode 100644 test/files/archive/ugoira.tar.7z create mode 100644 test/files/archive/ugoira.tar.gz create mode 100644 test/files/archive/zip-slip.zip diff --git a/app/logical/danbooru/archive.rb b/app/logical/danbooru/archive.rb index 2f33f9ecb..ec039b7f9 100644 --- a/app/logical/danbooru/archive.rb +++ b/app/logical/danbooru/archive.rb @@ -107,10 +107,15 @@ module Danbooru ensure archive&.close end - alias_method :entries, :each_entry - # Extract the files in the archive to a directory. Subdirectories inside the archive are ignored; all files are - # extracted to a single top-level directory. + # XXX You can't call `extract!` on these entries because libarchive doesn't let you extract an entry after you iterate past it. + # + # @return [Array] The list of entries in the archive. + def entries + @entries ||= each_entry.to_a + end + + # Extract the files in the archive to a directory. # # If a block is given, extract the archive to a temp directory and delete the directory after the block finishes. # Otherwise, extract to a temp directory and return the directory. The caller should delete the directory afterwards. @@ -135,15 +140,20 @@ module Danbooru # Extract the archive to a directory. See `extract!` for details. def extract_to!(directory, flags: DEFAULT_FLAGS) - entries.map do |entry| + each_entry.map do |entry| raise Danbooru::Archive::Error, "Can't extract archive containing absolute path (path: '#{entry.pathname_utf8}')" if entry.pathname_utf8.starts_with?("/") raise Danbooru::Archive::Error, "'#{entry.pathname_utf8}' is not a regular file" if !entry.file? - path = "#{directory}/#{entry.pathname_utf8.tr("/", "_")}" + path = "#{directory}/#{entry.pathname_utf8}" entry.extract!(path, flags: flags) end end + # @return [Integer] The number of files in the archive. + def file_count + @file_count ||= entries.count + end + # @return [Integer] The total decompressed size of all files in the archive. def uncompressed_size @uncompressed_size ||= entries.sum(&:size) @@ -151,7 +161,7 @@ module Danbooru # @return [Boolean] True if any entry in the archive satisfies the condition; otherwise false. def exists?(&block) - entries.with_index { |entry, index| return true if yield entry, index + 1 } + each_entry.with_index { |entry, index| return true if yield entry, index + 1 } false end @@ -162,7 +172,7 @@ module Danbooru # @return [String] The archive format as returned by libarchive ("RAR", "ZIP", etc). def format - @format ||= entries.lazy.map(&:format).first + @format ||= each_entry.lazy.map(&:format).first end # Print the archive contents in `ls -l` format. @@ -205,9 +215,15 @@ module Danbooru entry.pathname_utf8 end - # @return [String] The pathname encoded as UTF-8 instead of ASCII-8BIT. May be wrong if the original pathname wasn't UTF-8. + # @see https://security.snyk.io/research/zip-slip-vulnerability + # @return [Boolean] True if the pathname contains any ".." components, e.g. "../../../../../etc/passwd" + def directory_traversal? + entry.pathname.split("/").compact_blank.grep("..").any? + end + + # @return [String, nil] The pathname encoded as UTF-8 instead of ASCII-8BIT. May be wrong if the original pathname wasn't UTF-8. def pathname_utf8 - pathname.encode("UTF-8", invalid: :replace, undef: :replace, replace: "?") + pathname&.encode("UTF-8", invalid: :replace, undef: :replace, replace: "?") end # @return [String] The archive entry format ("RAR", "ZIP", etc). diff --git a/app/models/upload.rb b/app/models/upload.rb index f59fa1224..876f22a7b 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -4,6 +4,10 @@ class Upload < ApplicationRecord extend Memoist class Error < StandardError; end + # The list of allowed archive file types. + ARCHIVE_FILE_TYPES = %i[zip rar 7z] + + # The maximum number of files allowed per upload. MAX_FILES_PER_UPLOAD = 100 # The maximum number of 'pending' or 'processing' media assets a single user can have at once. @@ -18,11 +22,11 @@ class Upload < ApplicationRecord normalize :source, :normalize_source - validates :files, length: { maximum: MAX_FILES_PER_UPLOAD, message: "can't have more than #{MAX_FILES_PER_UPLOAD} files per upload" } validates :source, format: { with: %r{\Ahttps?://}i, message: "is not a valid URL" }, if: -> { source.present? } validates :referer_url, format: { with: %r{\Ahttps?://}i, message: "is not a valid URL" }, if: -> { referer_url.present? } validate :validate_file_and_source, on: :create - validate :uploader_is_not_limited, on: :create + validate :validate_archive_files, on: :create + validate :validate_uploader_is_not_limited, on: :create after_create :async_process_upload! @@ -69,13 +73,40 @@ class Upload < ApplicationRecord end end - def uploader_is_not_limited + def validate_uploader_is_not_limited queued_asset_count = uploader.upload_media_assets.unfinished.count if queued_asset_count > MAX_QUEUED_ASSETS errors.add(:base, "You have too many images queued for upload (queued: #{queued_asset_count}; limit: #{MAX_QUEUED_ASSETS}). Try again later.") end end + + def validate_archive_files + return unless files.present? + + archive_files.each do |archive, filename| + if !archive.file_ext.in?(ARCHIVE_FILE_TYPES) + errors.add(:base, "'#{filename}' is not a supported file type") + elsif archive.exists? { |_, count| count > MAX_FILES_PER_UPLOAD } + # XXX Potential zip bomb containing thousands of files; don't process it any further. + errors.add(:base, "'#{filename}' contains too many files (max #{MAX_FILES_PER_UPLOAD} files per upload)") + next + elsif archive.uncompressed_size > MediaAsset::MAX_FILE_SIZE + errors.add(:base, "'#{filename}' is too large (uncompressed size: #{archive.uncompressed_size.to_fs(:human_size)}; max size: #{MediaAsset::MAX_FILE_SIZE.to_fs(:human_size)})") + elsif entry = archive.entries.find { |entry| entry.pathname.starts_with?("/") } + errors.add(:base, "'#{entry.pathname_utf8}' in '#{filename}' can't start with '/'") + elsif entry = archive.entries.find { |entry| entry.directory_traversal? } + errors.add(:base, "'#{entry.pathname_utf8}' in '#{filename}' can't contain '..' components") + elsif entry = archive.entries.find { |entry| !entry.file? && !entry.directory? } + errors.add(:base, "'#{entry.pathname_utf8}' in '#{filename}' isn't a regular file") + end + end + + total_files = archive_files.map(&:first).sum(&:file_count) + (files.size - archive_files.size) + if total_files > MAX_FILES_PER_UPLOAD + errors.add(:base, "Can't upload more than #{MAX_FILES_PER_UPLOAD} files at a time (total: #{total_files})") + end + end end concerning :SourceMethods do @@ -132,30 +163,75 @@ class Upload < ApplicationRecord update!(status: "processing") if files.present? - upload_media_assets = files.map do |_index, file| - UploadMediaAsset.new(upload: self, file: file.tempfile, source_url: "file://#{file.original_filename}") - end + process_file_upload! elsif source.present? - page_url = source_extractor.page_url - image_urls = source_extractor.image_urls - - if image_urls.empty? - raise Error, "#{source} doesn't contain any images" - end - - upload_media_assets = image_urls.map do |image_url| - UploadMediaAsset.new(upload: self, source_url: image_url, page_url: page_url, media_asset: nil) - end + process_source_upload! else raise Error, "No file or source given" # Should never happen end + rescue Exception => e + update!(status: "error", error: e.message) + end + + def process_source_upload! + page_url = source_extractor.page_url + image_urls = source_extractor.image_urls + + if image_urls.empty? + raise Error, "#{source} doesn't contain any images" + end + + upload_media_assets = image_urls.map do |image_url| + UploadMediaAsset.new(upload: self, source_url: image_url, page_url: page_url, media_asset: nil) + end transaction do update!(media_asset_count: upload_media_assets.size) upload_media_assets.each(&:save!) end - rescue Exception => e - update!(status: "error", error: e.message) + end + + def process_file_upload! + tmpdirs = [] + + upload_media_assets = uploaded_files.flat_map do |file, original_filename| + if file.is_a?(Danbooru::Archive) + tmpdir, filenames = file.extract! + tmpdirs << tmpdir + + filenames.map do |filename| + name = "file://#{original_filename}/#{Pathname.new(filename).relative_path_from(tmpdir)}" # "file://foo.zip/foo/1.jpg" + UploadMediaAsset.new(upload: self, file: filename, source_url: name) + end + else + UploadMediaAsset.new(upload: self, file: file, source_url: "file://#{original_filename}") + end + end + + transaction do + update!(media_asset_count: upload_media_assets.size) + upload_media_assets.each(&:save!) + end + ensure + tmpdirs.each { |tmpdir| FileUtils.rm_rf(tmpdir) } + end + + # The list of files uploaded from disk, with their filenames. + def uploaded_files + files.map do |_index, file| + if FileTypeDetector.new(file.tempfile).file_ext.in?(ARCHIVE_FILE_TYPES) + [Danbooru::Archive.open!(file.tempfile), file.original_filename] + else + [MediaFile.open(file.tempfile), file.original_filename] + end + end + end + + # The list of archive files uploaded from disk, with their filenames. + def archive_files + uploaded_files.select do |file, original_filename| + file.is_a?(Danbooru::Archive) + end end def source_extractor @@ -167,5 +243,5 @@ class Upload < ApplicationRecord [:uploader, :upload_media_assets, :media_assets, :posts] end - memoize :source_extractor + memoize :source_extractor, :archive_files, :uploaded_files end diff --git a/test/files/archive/42.zip b/test/files/archive/42.zip new file mode 100644 index 0000000000000000000000000000000000000000..e7681537ced5f7abcc7dfb999da856951911649d GIT binary patch literal 42838 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 WZDC~tRUQHi0t^Xk3=9tzfOr5Y+|CvN literal 0 HcmV?d00001 diff --git a/test/files/archive/absolute-path.7z b/test/files/archive/absolute-path.7z new file mode 100644 index 0000000000000000000000000000000000000000..d4c92ac7136b1087feb39a3cb3390bbee47fa025 GIT binary patch literal 106 zcmXr7+Ou9=hJmHgMX+4G#h6;uf1_lO61`#es28Ix+_b{cXFD^Y=-D0PBnT-F+{kvXd>rwD`{SO8P2LAW)9~c-I zhwY_vBv z+8Z0~jg9ukMtftUy|K|2(P)ckv_&)!Euzuh*l5`^TK0^VJp)npj1Iz%4#JKO!VbtF z>}b{-&3dC*Z#3(TE)pJ8i-bpeW23#X(cai-Z)~(THaa&pIyW{tH#RyqHaa&p+9Dcl z5skKp2BJkY+8Y}!dq&Hi(XwYC%AU~y`q2UU(E<9=0s7Ga`av~7KUyr07R#f>@_-b} zqrI`w-q>hwY_vBv+8Z048ylS)8=V^)of{jS8yjsAjkbtJTSNoVA{yh!)XkZ)~*e87+H8%btNKdqxN7M+fLf2k1u!=tl?W z2h{-mXt6w6ERPn;15zxH_QpngW23#X(cai-Z)|jKY;?S=%`H#9J?G&D4PX=Z3> mX3DtmGNr+pk=0PIq@sk2SvYlHN%9w$9xd7Ous~*376t$`VP)U| literal 0 HcmV?d00001 diff --git a/test/files/archive/bomb-100-10M.rar b/test/files/archive/bomb-100-10M.rar new file mode 100644 index 0000000000000000000000000000000000000000..ffcdac45006d02ccf5a2a69aec1d6c9ee6a6961b GIT binary patch literal 61529 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 literal 0 HcmV?d00001 diff --git a/test/files/archive/bomb-10k-files.7z b/test/files/archive/bomb-10k-files.7z new file mode 100644 index 0000000000000000000000000000000000000000..58bc0b0171c29a7c69dd0d47f3ea2b134248bd59 GIT binary patch literal 4678 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 literal 0 HcmV?d00001 diff --git a/test/files/archive/symlink.zip b/test/files/archive/symlink.zip new file mode 100644 index 0000000000000000000000000000000000000000..0cea07dd8859d3641250168acb76db995fa96cf7 GIT binary patch literal 173 zcmWIWW@h1H0D5rV@|xTX3=AO5#vsE`kXT$?o)Q|u$-o@tTbdm0Tbf*2!Og(P z@`9Ox0ZizpmL%&#lm&P*GRZOHGE4$w2N?ff2%;c*LPJ;~Mxz-U;LXYgQpL!?$l$}k Kz~BwyFaQ8@u^$xx literal 0 HcmV?d00001 diff --git a/test/files/archive/ugoira.tar b/test/files/archive/ugoira.tar new file mode 100644 index 0000000000000000000000000000000000000000..a49f64cb5466e048dc91d5a46656096849383b39 GIT binary patch literal 20480 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 IfGQyX0BCxaF8}}l literal 0 HcmV?d00001 diff --git a/test/files/archive/ugoira.tar.7z b/test/files/archive/ugoira.tar.7z deleted file mode 100644 index e6c7033b6ef5813d5d3a64bc8fc7d3ab208899a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5084 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 diff --git a/test/files/archive/ugoira.tar.gz b/test/files/archive/ugoira.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..3fa92ba6a14a118c32b4c64062ce60c25339e82f GIT binary patch literal 5151 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 literal 0 HcmV?d00001 diff --git a/test/functional/uploads_controller_test.rb b/test/functional/uploads_controller_test.rb index 0b7af84be..003d312da 100644 --- a/test/functional/uploads_controller_test.rb +++ b/test/functional/uploads_controller_test.rb @@ -134,7 +134,7 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest should "fail if given both a file and source" do assert_no_difference("Upload.count") do - file = File.open("test/files/test.jpg") + file = Rack::Test::UploadedFile.new("test/files/test.jpg") source = "https://files.catbox.moe/om3tcw.webm" post_auth uploads_path(format: :json), @user, params: { upload: { files: { "0" => file }, source: source }} end @@ -296,6 +296,48 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest end end + context "for an unsupported archive type" do + should "fail for a .tar file" do + create_upload!("test/files/archive/ugoira.tar", user: @user) + assert_match("File is not an image or video", Upload.last.error) + end + + should "fail for a .tar.gz file" do + create_upload!("test/files/archive/ugoira.tar.gz", user: @user) + assert_match("File is not an image or video", Upload.last.error) + end + + should "fail for an archive containing more than 100 files" do + create_upload!("test/files/archive/bomb-10k-files.7z", user: @user) + assert_response 422 + assert_match("'bomb-10k-files.7z' contains too many files (max 100 files per upload)", response.parsed_body.dig("errors", "base", 0)) + end + + should "fail for a decompression bomb" do + create_upload!("test/files/archive/bomb-1-1G.rar", user: @user) + assert_response 422 + assert_match("'bomb-1-1G.rar' is too large (uncompressed size: 1,000 MB; max size: 100 MB)", response.parsed_body.dig("errors", "base", 0)) + end + + should "fail for an archive containing absolute paths" do + create_upload!("test/files/archive/absolute-path.7z", user: @user) + assert_response 422 + assert_match("'/tmp/foo/foo.txt' in 'absolute-path.7z' can't start with '/'", response.parsed_body.dig("errors", "base", 0)) + end + + should "fail for an archive containing '..' paths" do + create_upload!("test/files/archive/zip-slip.zip", user: @user) + assert_response 422 + assert_match(/'.*' in 'zip-slip\.zip' can't contain '\.\.' components/, response.parsed_body.dig("errors", "base", 0)) + end + + should "fail for an archive containing symlinks" do + create_upload!("test/files/archive/symlink.zip", user: @user) + assert_response 422 + assert_match("'passwd' in 'symlink.zip' isn't a regular file", response.parsed_body.dig("errors", "base", 0)) + end + end + context "when re-uploading a media asset stuck in the 'processing' state" do should "mark the asset as failed" do asset = create(:media_asset, file: File.open("test/files/test.jpg"), status: "processing") @@ -395,6 +437,36 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest should_upload_successfully("test/files/webp/lossy_alpha1.webp") end + context "uploading a .zip file from your computer" do + should "work" do + upload = assert_successful_upload("test/files/archive/ugoira.zip", user: @user) + + assert_equal(5, upload.media_asset_count) + assert_equal(5, upload.upload_media_assets.size) + assert_equal("file://ugoira.zip/000000.jpg", upload.upload_media_assets[0].source_url) + end + end + + context "uploading a .rar file from your computer" do + should "work" do + upload = assert_successful_upload("test/files/archive/ugoira.rar", user: @user) + + assert_equal(5, upload.media_asset_count) + assert_equal(5, upload.upload_media_assets.size) + assert_equal("file://ugoira.rar/000000.jpg", upload.upload_media_assets[0].source_url) + end + end + + context "uploading a .7z file from your computer" do + should "work" do + upload = assert_successful_upload("test/files/archive/ugoira.7z", user: @user) + + assert_equal(5, upload.media_asset_count) + assert_equal(5, upload.upload_media_assets.size) + assert_equal("file://ugoira.7z/000000.jpg", upload.upload_media_assets[0].source_url) + end + end + context "uploading multiple files from your computer" do should "work" do files = { diff --git a/test/unit/danbooru_archive_test.rb b/test/unit/danbooru_archive_test.rb index a86fd1c8a..318a6be9a 100644 --- a/test/unit/danbooru_archive_test.rb +++ b/test/unit/danbooru_archive_test.rb @@ -117,7 +117,8 @@ class DanbooruArchiveTest < ActiveSupport::TestCase assert_equal("ZIP 2.0 (uncompressed)", Danbooru::Archive.open("test/files/archive/ugoira.zip").format) assert_equal("RAR5", Danbooru::Archive.open("test/files/archive/ugoira.rar").format) assert_equal("7-Zip", Danbooru::Archive.open("test/files/archive/ugoira.7z").format) - assert_equal("7-Zip", Danbooru::Archive.open("test/files/archive/ugoira.tar.7z").format) + assert_equal("GNU tar format", Danbooru::Archive.open("test/files/archive/ugoira.tar").format) + assert_equal("GNU tar format", Danbooru::Archive.open("test/files/archive/ugoira.tar.gz").format) end end @@ -126,7 +127,8 @@ class DanbooruArchiveTest < ActiveSupport::TestCase assert_equal(:zip, Danbooru::Archive.open("test/files/archive/ugoira.zip").file_ext) assert_equal(:rar, Danbooru::Archive.open("test/files/archive/ugoira.rar").file_ext) assert_equal(:"7z", Danbooru::Archive.open("test/files/archive/ugoira.7z").file_ext) - assert_equal(:"7z", Danbooru::Archive.open("test/files/archive/ugoira.tar.7z").file_ext) + assert_equal(:bin, Danbooru::Archive.open("test/files/archive/ugoira.tar").file_ext) + assert_equal(:bin, Danbooru::Archive.open("test/files/archive/ugoira.tar.gz").file_ext) end end @@ -139,5 +141,40 @@ class DanbooruArchiveTest < ActiveSupport::TestCase assert_match(/^-rw-rw-r-- *0 0 *1639 2014-10-05 23:31:06 000000\.jpg$/, output.tap(&:rewind).read) end end + + should "detect directory traversal attacks" do + archive = Danbooru::Archive.open!("test/files/archive/zip-slip.zip") + + assert_equal(true, archive.entries.any? { |e| e.directory_traversal? }) + assert_raises(Danbooru::Archive::Error) { archive.extract! } + end + + should "detect symlinks" do + archive = Danbooru::Archive.open!("test/files/archive/symlink.zip") + + assert_equal(true, archive.entries.any? { |e| !e.file? && !e.directory? }) + assert_raises(Danbooru::Archive::Error) { archive.extract! } + end + + should "detect absolute paths" do + archive = Danbooru::Archive.open!("test/files/archive/absolute-path.7z") + + assert_equal(true, archive.entries.any? { |e| e.pathname.starts_with?("/") }) + assert_raises(Danbooru::Archive::Error) { archive.extract! } + end + + should "detect archives with large numbers of files" do + archive = Danbooru::Archive.open!("test/files/archive/bomb-10k-files.7z") + + assert_equal(true, archive.exists? { |_, count| count > 100 }) + assert_equal(10_000, archive.file_count) + end + + should "detect decompression bombs" do + archive = Danbooru::Archive.open!("test/files/archive/bomb-1-1G.rar") + + assert_equal(1, archive.file_count) + assert_equal(1_048_576_000, archive.uncompressed_size) + end end end